From cc7513b6637dc3cd92bdcf098f06cf8642864d22 Mon Sep 17 00:00:00 2001
From: Daniil Manin <manindaniil@gmail.com>
Date: Mon, 4 Oct 2021 17:50:17 +0700
Subject: [PATCH] #769 Update SWXMLHash

---
 .../{SWXMLHash.swift => XMLHash.swift}        | 396 +++++++++++++-----
 ...XMLIndexer+XMLIndexerDeserializable.swift} | 259 ++++++++++--
 Dependencies/SWXMLHash/shim.swift             |  36 ++
 Macaw.xcodeproj/project.pbxproj               |  30 +-
 Source/svg/CSSParser.swift                    |   2 +-
 Source/svg/SVGParser.swift                    |  30 +-
 6 files changed, 597 insertions(+), 156 deletions(-)
 rename Dependencies/SWXMLHash/{SWXMLHash.swift => XMLHash.swift} (69%)
 mode change 100755 => 100644
 rename Dependencies/SWXMLHash/{SWXMLHash+TypeConversion.swift => XMLIndexer+XMLIndexerDeserializable.swift} (71%)
 mode change 100755 => 100644
 create mode 100644 Dependencies/SWXMLHash/shim.swift

diff --git a/Dependencies/SWXMLHash/SWXMLHash.swift b/Dependencies/SWXMLHash/XMLHash.swift
old mode 100755
new mode 100644
similarity index 69%
rename from Dependencies/SWXMLHash/SWXMLHash.swift
rename to Dependencies/SWXMLHash/XMLHash.swift
index 1ad42d73..5ea2acf6
--- a/Dependencies/SWXMLHash/SWXMLHash.swift
+++ b/Dependencies/SWXMLHash/XMLHash.swift
@@ -1,5 +1,5 @@
 //
-//  SWXMLHash.swift
+//  XMLHash.swift
 //  SWXMLHash
 //
 //  Copyright (c) 2014 David Mohundro
@@ -30,11 +30,14 @@
 // swiftlint:disable file_length
 
 import Foundation
+#if canImport(FoundationXML)
+import FoundationXML
+#endif
 
 let rootElementName = "SWXMLHash_Root_Element"
 
 /// Parser options
-public class SWXMLHashOptions {
+public class XMLHashOptions {
     internal init() {}
 
     /// determines whether to parse the XML with lazy parsing or not
@@ -50,13 +53,20 @@ public class SWXMLHashOptions {
 
     /// Encoding used for XML parsing. Default is set to UTF8
     public var encoding = String.Encoding.utf8
+
+    /// Any contextual information set by the user for encoding
+    public var userInfo = [CodingUserInfoKey: Any]()
+
+    /// Detect XML parsing errors... defaults to false as this library will
+    /// attempt to handle HTML which isn't always XML-compatible
+    public var detectParsingErrors = false
 }
 
 /// Simple XML parser
-public class SWXMLHash {
-    let options: SWXMLHashOptions
+public class XMLHash {
+    let options: XMLHashOptions
 
-    private init(_ options: SWXMLHashOptions = SWXMLHashOptions()) {
+    private init(_ options: XMLHashOptions = XMLHashOptions()) {
         self.options = options
     }
 
@@ -64,14 +74,14 @@ public class SWXMLHash {
     Method to configure how parsing works.
 
     - parameters:
-        - configAction: a block that passes in an `SWXMLHashOptions` object with
+        - configAction: a block that passes in an `XMLHashOptions` object with
         options to be set
-    - returns: an `SWXMLHash` instance
+    - returns: an `XMLHash` instance
     */
-    class public func config(_ configAction: (SWXMLHashOptions) -> Void) -> SWXMLHash {
-        let opts = SWXMLHashOptions()
+    class public func config(_ configAction: (XMLHashOptions) -> Void) -> XMLHash {
+        let opts = XMLHashOptions()
         configAction(opts)
-        return SWXMLHash(opts)
+        return XMLHash(opts)
     }
 
     /**
@@ -110,7 +120,7 @@ public class SWXMLHash {
     - returns: An XMLIndexer instance that is used to look up elements in the XML
     */
     class public func parse(_ xml: String) -> XMLIndexer {
-        return SWXMLHash().parse(xml)
+        XMLHash().parse(xml)
     }
 
     /**
@@ -120,7 +130,7 @@ public class SWXMLHash {
     - returns: An XMLIndexer instance that is used to look up elements in the XML
     */
     class public func parse(_ data: Data) -> XMLIndexer {
-        return SWXMLHash().parse(data)
+        XMLHash().parse(data)
     }
 
     /**
@@ -130,7 +140,7 @@ public class SWXMLHash {
     - returns: An XMLIndexer instance that is used to look up elements in the XML
     */
     class public func lazy(_ xml: String) -> XMLIndexer {
-        return config { conf in conf.shouldProcessLazily = true }.parse(xml)
+        config { conf in conf.shouldProcessLazily = true }.parse(xml)
     }
 
     /**
@@ -140,128 +150,137 @@ public class SWXMLHash {
     - returns: An XMLIndexer instance that is used to look up elements in the XML
     */
     class public func lazy(_ data: Data) -> XMLIndexer {
-        return config { conf in conf.shouldProcessLazily = true }.parse(data)
+        config { conf in conf.shouldProcessLazily = true }.parse(data)
     }
 }
 
 struct Stack<T> {
     var items = [T]()
+
     mutating func push(_ item: T) {
         items.append(item)
     }
+
     mutating func pop() -> T {
-        return items.removeLast()
+        items.removeLast()
     }
+
     mutating func drop() {
         _ = pop()
     }
+
     mutating func removeAll() {
         items.removeAll(keepingCapacity: false)
     }
+
     func top() -> T {
-        return items[items.count - 1]
+        items[items.count - 1]
     }
 }
 
 protocol SimpleXmlParser {
-    init(_ options: SWXMLHashOptions)
+    init(_ options: XMLHashOptions)
     func parse(_ data: Data) -> XMLIndexer
 }
 
 #if os(Linux)
 
 extension XMLParserDelegate {
+    func parserDidStartDocument(_ parser: XMLParser) { }
+    func parserDidEndDocument(_ parser: XMLParser) { }
 
-    func parserDidStartDocument(_ parser: Foundation.XMLParser) { }
-    func parserDidEndDocument(_ parser: Foundation.XMLParser) { }
-
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundNotationDeclarationWithName name: String,
                 publicID: String?,
                 systemID: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundUnparsedEntityDeclarationWithName name: String,
                 publicID: String?,
                 systemID: String?,
                 notationName: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundAttributeDeclarationWithName attributeName: String,
                 forElement elementName: String,
                 type: String?,
                 defaultValue: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundElementDeclarationWithName elementName: String,
                 model: String) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundInternalEntityDeclarationWithName name: String,
                 value: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundExternalEntityDeclarationWithName name: String,
                 publicID: String?,
                 systemID: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didStartElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?,
-                attributes attributeDict: [String : String]) { }
+                attributes attributeDict: [String: String]) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didEndElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didStartMappingPrefix prefix: String,
                 toURI namespaceURI: String) { }
 
-    func parser(_ parser: Foundation.XMLParser, didEndMappingPrefix prefix: String) { }
+    func parser(_ parser: XMLParser,
+                didEndMappingPrefix prefix: String) { }
 
-    func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) { }
+    func parser(_ parser: XMLParser,
+                foundCharacters string: String) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundIgnorableWhitespace whitespaceString: String) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 foundProcessingInstructionWithTarget target: String,
                 data: String?) { }
 
-    func parser(_ parser: Foundation.XMLParser, foundComment comment: String) { }
+    func parser(_ parser: XMLParser,
+                foundComment comment: String) { }
 
-    func parser(_ parser: Foundation.XMLParser, foundCDATA CDATABlock: Data) { }
+    func parser(_ parser: XMLParser,
+                foundCDATA CDATABlock: Data) { }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 resolveExternalEntityName name: String,
-                systemID: String?) -> Data? { return nil }
+                systemID: String?) -> Data? { nil }
 
-    func parser(_ parser: Foundation.XMLParser, parseErrorOccurred parseError: NSError) { }
+    func parser(_ parser: XMLParser,
+                parseErrorOccurred parseError: Error) { }
 
-    func parser(_ parser: Foundation.XMLParser,
-                validationErrorOccurred validationError: NSError) { }
+    func parser(_ parser: XMLParser,
+                validationErrorOccurred validationError: Error) { }
 }
 
 #endif
 
 /// The implementation of XMLParserDelegate and where the lazy parsing actually happens.
 class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
-    required init(_ options: SWXMLHashOptions) {
+    required init(_ options: XMLHashOptions) {
+        root = XMLElement(name: rootElementName, options: options)
         self.options = options
-        self.root.caseInsensitive = options.caseInsensitive
         super.init()
     }
 
-    var root = XMLElement(name: rootElementName, caseInsensitive: false)
+    var root: XMLElement
     var parentStack = Stack<XMLElement>()
     var elementStack = Stack<String>()
 
     var data: Data?
     var ops: [IndexOp] = []
-    let options: SWXMLHashOptions
+    let options: XMLHashOptions
 
     func parse(_ data: Data) -> XMLIndexer {
         self.data = data
@@ -269,25 +288,23 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
     }
 
     func startParsing(_ ops: [IndexOp]) {
-        // clear any prior runs of parse... expected that this won't be necessary,
-        // but you never know
+        // reset state for a new lazy parsing run
+        root = XMLElement(name: rootElementName, options: root.options)
         parentStack.removeAll()
-        root = XMLElement(name: rootElementName, caseInsensitive: options.caseInsensitive)
         parentStack.push(root)
 
         self.ops = ops
-        let parser = Foundation.XMLParser(data: data!)
+        let parser = XMLParser(data: data!)
         parser.shouldProcessNamespaces = options.shouldProcessNamespaces
         parser.delegate = self
         _ = parser.parse()
     }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didStartElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?,
                 attributes attributeDict: [String: String]) {
-
         elementStack.push(elementName)
 
         if !onMatch() {
@@ -300,7 +317,7 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
         parentStack.push(currentNode)
     }
 
-    func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) {
+    func parser(_ parser: XMLParser, foundCharacters string: String) {
         if !onMatch() {
             return
         }
@@ -310,11 +327,22 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
         current.addText(string)
     }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
+        if !onMatch() {
+            return
+        }
+
+        if let cdataText = String(data: CDATABlock, encoding: String.Encoding.utf8) {
+            let current = parentStack.top()
+
+            current.addText(cdataText)
+        }
+    }
+
+    func parser(_ parser: XMLParser,
                 didEndElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?) {
-
         let match = onMatch()
 
         elementStack.drop()
@@ -337,15 +365,16 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
 
 /// The implementation of XMLParserDelegate and where the parsing actually happens.
 class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
-    required init(_ options: SWXMLHashOptions) {
+    required init(_ options: XMLHashOptions) {
+        root = XMLElement(name: rootElementName, options: options)
         self.options = options
-        self.root.caseInsensitive = options.caseInsensitive
         super.init()
     }
 
-    var root = XMLElement(name: rootElementName, caseInsensitive: false)
+    let root: XMLElement
     var parentStack = Stack<XMLElement>()
-    let options: SWXMLHashOptions
+    let options: XMLHashOptions
+    var parsingError: ParsingError?
 
     func parse(_ data: Data) -> XMLIndexer {
         // clear any prior runs of parse... expected that this won't be necessary,
@@ -354,20 +383,23 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
 
         parentStack.push(root)
 
-        let parser = Foundation.XMLParser(data: data)
+        let parser = XMLParser(data: data)
         parser.shouldProcessNamespaces = options.shouldProcessNamespaces
         parser.delegate = self
         _ = parser.parse()
 
-        return XMLIndexer(root)
+        if options.detectParsingErrors, let err = parsingError {
+            return XMLIndexer.parsingError(err)
+        } else {
+            return XMLIndexer(root)
+        }
     }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didStartElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?,
                 attributes attributeDict: [String: String]) {
-
         let currentNode = parentStack
             .top()
             .addElement(elementName, withAttributes: attributeDict, caseInsensitive: self.options.caseInsensitive)
@@ -375,19 +407,39 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate {
         parentStack.push(currentNode)
     }
 
-    func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) {
+    func parser(_ parser: XMLParser, foundCharacters string: String) {
         let current = parentStack.top()
 
         current.addText(string)
     }
 
-    func parser(_ parser: Foundation.XMLParser,
+    func parser(_ parser: XMLParser,
                 didEndElement elementName: String,
                 namespaceURI: String?,
                 qualifiedName qName: String?) {
-
         parentStack.drop()
     }
+
+    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
+        if let cdataText = String(data: CDATABlock, encoding: String.Encoding.utf8) {
+            let current = parentStack.top()
+
+            current.addText(cdataText)
+        }
+    }
+
+    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
+#if os(Linux) && !swift(>=4.1.50)
+        if let err = parseError as? NSError {
+            parsingError = ParsingError(line: err.userInfo["NSXMLParserErrorLineNumber"] as? Int ?? 0,
+                                        column: err.userInfo["NSXMLParserErrorColumn"] as? Int ?? 0)
+        }
+#else
+        let err = parseError as NSError
+        parsingError = ParsingError(line: err.userInfo["NSXMLParserErrorLineNumber"] as? Int ?? 0,
+                                    column: err.userInfo["NSXMLParserErrorColumn"] as? Int ?? 0)
+#endif
+    }
 }
 
 /// Represents an indexed operation against a lazily parsed `XMLIndexer`
@@ -424,10 +476,10 @@ public class IndexOps {
         parser.startParsing(ops)
         let indexer = XMLIndexer(parser.root)
         var childIndex = indexer
-        for op in ops {
-            childIndex = childIndex[op.key]
-            if op.index >= 0 {
-                childIndex = childIndex[op.index]
+        for oper in ops {
+            childIndex = childIndex[oper.key]
+            if oper.index >= 0 {
+                childIndex = childIndex[oper.index]
             }
         }
         ops.removeAll(keepingCapacity: false)
@@ -435,14 +487,19 @@ public class IndexOps {
     }
 
     func stringify() -> String {
-        var s = ""
-        for op in ops {
-            s += "[" + op.toString() + "]"
+        var ret = ""
+        for oper in ops {
+            ret += "[" + oper.toString() + "]"
         }
-        return s
+        return ret
     }
 }
 
+public struct ParsingError: Error {
+    public let line: Int
+    public let column: Int
+}
+
 /// Error type that is thrown when an indexing or parsing operation fails.
 public enum IndexingError: Error {
     case attribute(attr: String)
@@ -459,22 +516,27 @@ public enum IndexingError: Error {
     public static func Attribute(attr: String) -> IndexingError {
         fatalError("unavailable")
     }
+
     @available(*, unavailable, renamed: "attributeValue(attr:value:)")
     public static func AttributeValue(attr: String, value: String) -> IndexingError {
         fatalError("unavailable")
     }
+
     @available(*, unavailable, renamed: "key(key:)")
     public static func Key(key: String) -> IndexingError {
         fatalError("unavailable")
     }
+
     @available(*, unavailable, renamed: "index(idx:)")
     public static func Index(idx: Int) -> IndexingError {
         fatalError("unavailable")
     }
+
     @available(*, unavailable, renamed: "initialize(instance:)")
     public static func Init(instance: AnyObject) -> IndexingError {
         fatalError("unavailable")
     }
+
     @available(*, unavailable, renamed: "error")
     public static var Error: IndexingError {
         fatalError("unavailable")
@@ -482,12 +544,13 @@ public enum IndexingError: Error {
 // swiftlint:enable identifier_name
 }
 
-/// Returned from SWXMLHash, allows easy element lookup into XML data.
+/// Returned from XMLHash, allows easy element lookup into XML data.
 public enum XMLIndexer {
     case element(XMLElement)
     case list([XMLElement])
     case stream(IndexOps)
     case xmlError(IndexingError)
+    case parsingError(ParsingError)
 
 // swiftlint:disable identifier_name
     // unavailable
@@ -528,18 +591,18 @@ public enum XMLIndexer {
 
     /// All elements at the currently indexed level
     public var all: [XMLIndexer] {
+        allElements.map { XMLIndexer($0) }
+    }
+
+    private var allElements: [XMLElement] {
         switch self {
         case .list(let list):
-            var xmlList = [XMLIndexer]()
-            for elem in list {
-                xmlList.append(XMLIndexer(elem))
-            }
-            return xmlList
+            return list
         case .element(let elem):
-            return [XMLIndexer(elem)]
+            return [elem]
         case .stream(let ops):
             let list = ops.findElements()
-            return list.all
+            return list.allElements
         default:
             return []
         }
@@ -547,15 +610,56 @@ public enum XMLIndexer {
 
     /// All child elements from the currently indexed level
     public var children: [XMLIndexer] {
-        var list = [XMLIndexer]()
+        childElements.map { XMLIndexer($0) }
+    }
+
+    private var childElements: [XMLElement] {
+        var list = [XMLElement]()
         for elem in all.compactMap({ $0.element }) {
             for elem in elem.xmlChildren {
-                list.append(XMLIndexer(elem))
+                list.append(elem)
             }
         }
         return list
     }
 
+    @available(*, unavailable, renamed: "filterChildren(_:)")
+    public func filter(_ included: (_ elem: XMLElement, _ index: Int) -> Bool) -> XMLIndexer {
+        filterChildren(included)
+    }
+
+    public func filterChildren(_ included: (_ elem: XMLElement, _ index: Int) -> Bool) -> XMLIndexer {
+        let children = handleFilteredResults(list: childElements, included: included)
+        if let current = self.element {
+            let filteredElem = XMLElement(name: current.name, index: current.index, options: current.options)
+            filteredElem.children = children.allElements
+            return .element(filteredElem)
+        }
+        return .xmlError(IndexingError.error)
+    }
+
+    public func filterAll(_ included: (_ elem: XMLElement, _ index: Int) -> Bool) -> XMLIndexer {
+        handleFilteredResults(list: allElements, included: included)
+    }
+
+    private func handleFilteredResults(list: [XMLElement],
+                                       included: (_ elem: XMLElement, _ index: Int) -> Bool) -> XMLIndexer {
+        let results = zip(list.indices, list).filter { included($1, $0) }.map { $1 }
+        if results.count == 1 {
+            return .element(results.first!)
+        }
+        return .list(results)
+    }
+
+    public var userInfo: [CodingUserInfoKey: Any] {
+        switch self {
+        case .element(let elem):
+            return elem.userInfo
+        default:
+            return [:]
+        }
+    }
+
     /**
     Allows for element lookup by matching attribute values.
 
@@ -627,8 +731,8 @@ public enum XMLIndexer {
     public func byKey(_ key: String) throws -> XMLIndexer {
         switch self {
         case .stream(let opStream):
-            let op = IndexOp(key)
-            opStream.ops.append(op)
+            let oper = IndexOp(key)
+            opStream.ops.append(oper)
             return .stream(opStream)
         case .element(let elem):
             let match = elem.xmlChildren.filter({
@@ -641,7 +745,7 @@ public enum XMLIndexer {
                     return .list(match)
                 }
             }
-            fallthrough
+            throw IndexingError.key(key: key)
         default:
             throw IndexingError.key(key: key)
         }
@@ -676,7 +780,7 @@ public enum XMLIndexer {
             opStream.ops[opStream.ops.count - 1].index = index
             return .stream(opStream)
         case .list(let list):
-            if index <= list.count {
+            if index < list.count {
                 return .element(list[index])
             }
             return .xmlError(IndexingError.index(idx: index))
@@ -684,7 +788,7 @@ public enum XMLIndexer {
             if index == 0 {
                 return .element(elem)
             }
-            fallthrough
+            return .xmlError(IndexingError.index(idx: index))
         default:
             return .xmlError(IndexingError.index(idx: index))
         }
@@ -733,7 +837,7 @@ extension IndexingError: CustomStringConvertible {
         switch self {
         case .attribute(let attr):
             return "XML Attribute Error: Missing attribute [\"\(attr)\"]"
-        case .attributeValue(let attr, let value):
+        case let .attributeValue(attr, value):
             return "XML Attribute Error: Missing attribute [\"\(attr)\"] with value [\"\(value)\"]"
         case .key(let key):
             return "XML Element Error: Incorrect key [\"\(key)\"]"
@@ -756,6 +860,7 @@ public protocol XMLContent: CustomStringConvertible { }
 public class TextElement: XMLContent {
     /// The underlying text value
     public let text: String
+
     init(text: String) {
         self.text = text
     }
@@ -764,6 +869,7 @@ public class TextElement: XMLContent {
 public struct XMLAttribute {
     public let name: String
     public let text: String
+
     init(name: String, text: String) {
         self.name = name
         self.text = text
@@ -775,11 +881,19 @@ public class XMLElement: XMLContent {
     /// The name of the element
     public let name: String
 
-    public var caseInsensitive: Bool
+    /// Whether the element is case insensitive or not
+    public var caseInsensitive: Bool {
+        options.caseInsensitive
+    }
+
+    var userInfo: [CodingUserInfoKey: Any] {
+        options.userInfo
+    }
 
     /// All attributes
     public var allAttributes = [String: XMLAttribute]()
 
+    /// Find an attribute by name
     public func attribute(by name: String) -> XMLAttribute? {
         if caseInsensitive {
             return allAttributes.first(where: { $0.key.compare(name, true) })?.value
@@ -789,7 +903,7 @@ public class XMLElement: XMLContent {
 
     /// The inner text of the element, if it exists
     public var text: String {
-        return children.reduce("", {
+        children.reduce("", {
             if let element = $1 as? TextElement {
                 return $0 + element.text
             }
@@ -800,7 +914,7 @@ public class XMLElement: XMLContent {
 
     /// The inner text of the element and its children
     public var recursiveText: String {
-        return children.reduce("", {
+        children.reduce("", {
             if let textElement = $1 as? TextElement {
                 return $0 + textElement.text
             } else if let xmlElement = $1 as? XMLElement {
@@ -811,13 +925,21 @@ public class XMLElement: XMLContent {
         })
     }
 
+    public var innerXML: String {
+        children.reduce("", {
+            $0.description + $1.description
+        })
+    }
+
     /// All child elements (text or XML)
     public var children = [XMLContent]()
+
     var count: Int = 0
     var index: Int
+    let options: XMLHashOptions
 
     var xmlChildren: [XMLElement] {
-        return children.compactMap { $0 as? XMLElement }
+        children.compactMap { $0 as? XMLElement }
     }
 
     /**
@@ -826,11 +948,12 @@ public class XMLElement: XMLContent {
     - parameters:
         - name: The name of the element to be initialized
         - index: The index of the element to be initialized
+        - options: The XMLHash options
     */
-    init(name: String, index: Int = 0, caseInsensitive: Bool) {
+    init(name: String, index: Int = 0, options: XMLHashOptions) {
         self.name = name
-        self.caseInsensitive = caseInsensitive
         self.index = index
+        self.options = options
     }
 
     /**
@@ -843,7 +966,7 @@ public class XMLElement: XMLContent {
     */
 
     func addElement(_ name: String, withAttributes attributes: [String: String], caseInsensitive: Bool) -> XMLElement {
-        let element = XMLElement(name: name, index: count, caseInsensitive: caseInsensitive)
+        let element = XMLElement(name: name, index: count, options: options)
         count += 1
 
         children.append(element)
@@ -865,14 +988,14 @@ public class XMLElement: XMLContent {
 extension TextElement: CustomStringConvertible {
     /// The text value for a `TextElement` instance.
     public var description: String {
-        return text
+        text
     }
 }
 
 extension XMLAttribute: CustomStringConvertible {
     /// The textual representation of an `XMLAttribute` instance.
     public var description: String {
-        return "\(name)=\"\(text)\""
+        "\(name)=\"\(text)\""
     }
 }
 
@@ -888,7 +1011,7 @@ extension XMLElement: CustomStringConvertible {
                 xmlReturn.append(child.description)
             }
             xmlReturn.append("</\(name)>")
-            return xmlReturn.joined(separator: "")
+            return xmlReturn.joined()
         }
 
         return "<\(name)\(attributesString)>\(text)</\(name)>"
@@ -900,11 +1023,11 @@ extension XMLElement: CustomStringConvertible {
 // On macOS, `XMLElement` is defined in Foundation.
 // So, the code referencing `XMLElement` generates above error.
 // Following code allow to using `SWXMLhash.XMLElement` in client codes.
-extension SWXMLHash {
-    public typealias XMLElement = SWXMLHashXMLElement
+extension XMLHash {
+    public typealias XMLElement = XMLHashXMLElement
 }
 
-public typealias SWXMLHashXMLElement = XMLElement
+public typealias XMLHashXMLElement = XMLElement
 
 fileprivate extension String {
     func compare(_ str2: String?, _ insensitive: Bool) -> Bool {
@@ -918,3 +1041,72 @@ fileprivate extension String {
         return str1 == str2
     }
 }
+
+// MARK: - XMLIndexer String RawRepresentables
+
+/*: Provides XMLIndexer Serialization/Deserialization using String backed RawRepresentables
+    Added by [PeeJWeeJ](https://github.com/PeeJWeeJ) */
+extension XMLIndexer {
+    /**
+     Allows for element lookup by matching attribute values
+     using a String backed RawRepresentables (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for withAttribute(String, String)
+
+     - parameters:
+     - attr: should the name of the attribute to match on
+     - value: should be the value of the attribute to match on
+     - throws: an XMLIndexer.XMLError if an element with the specified attribute isn't found
+     - returns: instance of XMLIndexer
+     */
+    public func withAttribute<A: RawRepresentable, V: RawRepresentable>(_ attr: A, _ value: V) throws -> XMLIndexer
+        where A.RawValue == String, V.RawValue == String {
+        try withAttribute(attr.rawValue, value.rawValue)
+    }
+
+    /**
+     Find an XML element at the current level by element name
+     using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for byKey(String)
+
+     - parameter key: The element name to index by
+     - returns: instance of XMLIndexer to match the element (or elements) found by key
+     - throws: Throws an XMLIndexingError.Key if no element was found
+     */
+    public func byKey<K: RawRepresentable>(_ key: K) throws -> XMLIndexer where K.RawValue == String {
+        try byKey(key.rawValue)
+    }
+
+    /**
+     Find an XML element at the current level by element name
+     using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for self[String]
+
+     - parameter key: The element name to index by
+     - returns: instance of XMLIndexer to match the element (or elements) found by
+     */
+    public subscript<K: RawRepresentable>(key: K) -> XMLIndexer where K.RawValue == String {
+        self[key.rawValue]
+    }
+}
+
+// MARK: - XMLElement String RawRepresentables
+
+/*: Provides XMLIndexer Serialization/Deserialization using String backed RawRepresentables
+ Added by [PeeJWeeJ](https://github.com/PeeJWeeJ) */
+extension XMLElement {
+    /**
+     Find an attribute by name using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for self[String]
+     */
+    public func attribute<N: RawRepresentable>(by name: N) -> XMLAttribute? where N.RawValue == String {
+        attribute(by: name.rawValue)
+    }
+}
diff --git a/Dependencies/SWXMLHash/SWXMLHash+TypeConversion.swift b/Dependencies/SWXMLHash/XMLIndexer+XMLIndexerDeserializable.swift
old mode 100755
new mode 100644
similarity index 71%
rename from Dependencies/SWXMLHash/SWXMLHash+TypeConversion.swift
rename to Dependencies/SWXMLHash/XMLIndexer+XMLIndexerDeserializable.swift
index 28f12103..2ddb8f31
--- a/Dependencies/SWXMLHash/SWXMLHash+TypeConversion.swift
+++ b/Dependencies/SWXMLHash/XMLIndexer+XMLIndexerDeserializable.swift
@@ -1,5 +1,5 @@
 //
-//  SWXMLHash+TypeConversion.swift
+//  XMLHash+TypeConversion.swift
 //  SWXMLHash
 //
 //  Copyright (c) 2016 Maciek Grzybowskio
@@ -34,6 +34,8 @@ import Foundation
 public protocol XMLIndexerDeserializable {
     /// Method for deserializing elements from XMLIndexer
     static func deserialize(_ element: XMLIndexer) throws -> Self
+    /// Method for validating elements post deserialization
+    func validate() throws
 }
 
 /// Provides XMLIndexer deserialization / type transformation support
@@ -50,6 +52,12 @@ public extension XMLIndexerDeserializable {
         throw XMLDeserializationError.implementationIsMissing(
             method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)")
     }
+
+    /**
+    A default do nothing implementation of validation.
+    - throws: nothing
+    */
+    func validate() throws {}
 }
 
 // MARK: - XMLElementDeserializable
@@ -58,6 +66,8 @@ public extension XMLIndexerDeserializable {
 public protocol XMLElementDeserializable {
     /// Method for deserializing elements from XMLElement
     static func deserialize(_ element: XMLElement) throws -> Self
+    /// Method for validating elements from XMLElement post deserialization
+    func validate() throws
 }
 
 /// Provides XMLElement deserialization / type transformation support
@@ -74,13 +84,22 @@ public extension XMLElementDeserializable {
         throw XMLDeserializationError.implementationIsMissing(
             method: "XMLElementDeserializable.deserialize(element: XMLElement)")
     }
+
+    /**
+    A default do nothing implementation of validation.
+    - throws: nothing
+    */
+    func validate() throws {}
 }
 
 // MARK: - XMLAttributeDeserializable
 
 /// Provides XMLAttribute deserialization / type transformation support
 public protocol XMLAttributeDeserializable {
+    /// Method for deserializing elements from XMLAttribute
     static func deserialize(_ attribute: XMLAttribute) throws -> Self
+    /// Method for validating elements from XMLAttribute post deserialization
+    func validate() throws
 }
 
 /// Provides XMLAttribute deserialization / type transformation support
@@ -97,12 +116,16 @@ public extension XMLAttributeDeserializable {
         throw XMLDeserializationError.implementationIsMissing(
             method: "XMLAttributeDeserializable(element: XMLAttribute)")
     }
+    /**
+    A default do nothing implementation of validation.
+    - throws: nothing
+    */
+    func validate() throws {}
 }
 
 // MARK: - XMLIndexer Extensions
 
 public extension XMLIndexer {
-
     // MARK: - XMLAttributeDeserializable
 
     /**
@@ -216,7 +239,9 @@ public extension XMLIndexer {
     func value<T: XMLElementDeserializable>() throws -> T {
         switch self {
         case .element(let element):
-            return try T.deserialize(element)
+            let deserialized = try T.deserialize(element)
+            try deserialized.validate()
+            return deserialized
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -233,7 +258,9 @@ public extension XMLIndexer {
     func value<T: XMLElementDeserializable>() throws -> T? {
         switch self {
         case .element(let element):
-            return try T.deserialize(element)
+            let deserialized = try T.deserialize(element)
+            try deserialized.validate()
+            return deserialized
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -250,9 +277,17 @@ public extension XMLIndexer {
     func value<T: XMLElementDeserializable>() throws -> [T] {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize($0) }
+            return try elements.map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize($0) }
+            return try [element].map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -269,9 +304,17 @@ public extension XMLIndexer {
     func value<T: XMLElementDeserializable>() throws -> [T]? {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize($0) }
+            return try elements.map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize($0) }
+            return try [element].map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -288,9 +331,17 @@ public extension XMLIndexer {
     func value<T: XMLElementDeserializable>() throws -> [T?] {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize($0) }
+            return try elements.map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize($0) }
+            return try [element].map {
+                let deserialized = try T.deserialize($0)
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -309,7 +360,9 @@ public extension XMLIndexer {
     func value<T: XMLIndexerDeserializable>() throws -> T {
         switch self {
         case .element:
-            return try T.deserialize(self)
+            let deserialized = try T.deserialize(self)
+            try deserialized.validate()
+            return deserialized
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -326,7 +379,9 @@ public extension XMLIndexer {
     func value<T: XMLIndexerDeserializable>() throws -> T? {
         switch self {
         case .element:
-            return try T.deserialize(self)
+            let deserialized = try T.deserialize(self)
+            try deserialized.validate()
+            return deserialized
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -343,9 +398,17 @@ public extension XMLIndexer {
     func value<T>() throws -> [T] where T: XMLIndexerDeserializable {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
+            return try elements.map {
+                let deserialized = try T.deserialize( XMLIndexer($0) )
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
+            return try [element].map {
+                let deserialized = try T.deserialize( XMLIndexer($0) )
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -362,9 +425,17 @@ public extension XMLIndexer {
     func value<T: XMLIndexerDeserializable>() throws -> [T]? {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
+            return try elements.map {
+                let deserialized = try T.deserialize(XMLIndexer($0))
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
+            return try [element].map {
+                let deserialized = try T.deserialize(XMLIndexer($0))
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -381,9 +452,17 @@ public extension XMLIndexer {
     func value<T: XMLIndexerDeserializable>() throws -> [T?] {
         switch self {
         case .list(let elements):
-            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
+            return try elements.map {
+                let deserialized = try T.deserialize(XMLIndexer($0))
+                try deserialized.validate()
+                return deserialized
+            }
         case .element(let element):
-            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
+            return try [element].map {
+                let deserialized = try T.deserialize(XMLIndexer($0))
+                try deserialized.validate()
+                return deserialized
+            }
         case .stream(let opStream):
             return try opStream.findElements().value()
         default:
@@ -392,10 +471,89 @@ public extension XMLIndexer {
     }
 }
 
+// MARK: - XMLAttributeDeserializable String RawRepresentable
+
+/*: Provides XMLIndexer XMLAttributeDeserializable deserialization from String backed RawRepresentables
+    Added by [PeeJWeeJ](https://github.com/PeeJWeeJ) */
+public extension XMLIndexer {
+    /**
+     Attempts to deserialize the value of the specified attribute of the current XMLIndexer
+     element to `T` using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - throws: an XMLDeserializationError if there is a problem with deserialization
+     - returns: The deserialized `T` value
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) throws -> T where A.RawValue == String {
+        try value(ofAttribute: attr.rawValue)
+    }
+
+    /**
+     Attempts to deserialize the value of the specified attribute of the current XMLIndexer
+     element to `T?` using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - returns: The deserialized `T?` value, or nil if the attribute does not exist
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) -> T? where A.RawValue == String {
+        value(ofAttribute: attr.rawValue)
+    }
+
+    /**
+     Attempts to deserialize the value of the specified attribute of the current XMLIndexer
+     element to `[T]` using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - throws: an XMLDeserializationError if there is a problem with deserialization
+     - returns: The deserialized `[T]` value
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) throws -> [T] where A.RawValue == String {
+        try value(ofAttribute: attr.rawValue)
+    }
+
+    /**
+     Attempts to deserialize the value of the specified attribute of the current XMLIndexer
+     element to `[T]?` using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - throws: an XMLDeserializationError if there is a problem with deserialization
+     - returns: The deserialized `[T]?` value
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) throws -> [T]? where A.RawValue == String {
+        try value(ofAttribute: attr.rawValue)
+    }
+
+    /**
+     Attempts to deserialize the value of the specified attribute of the current XMLIndexer
+     element to `[T?]` using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - throws: an XMLDeserializationError if there is a problem with deserialization
+     - returns: The deserialized `[T?]` value
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) throws -> [T?] where A.RawValue == String {
+        try value(ofAttribute: attr.rawValue)
+    }
+}
+
 // MARK: - XMLElement Extensions
 
 extension XMLElement {
-
     /**
      Attempts to deserialize the specified attribute of the current XMLElement to `T`
 
@@ -405,7 +563,9 @@ extension XMLElement {
      */
     public func value<T: XMLAttributeDeserializable>(ofAttribute attr: String) throws -> T {
         if let attr = self.attribute(by: attr) {
-            return try T.deserialize(attr)
+            let deserialized = try T.deserialize(attr)
+            try deserialized.validate()
+            return deserialized
         } else {
             throw XMLDeserializationError.attributeDoesNotExist(element: self, attribute: attr)
         }
@@ -419,7 +579,9 @@ extension XMLElement {
      */
     public func value<T: XMLAttributeDeserializable>(ofAttribute attr: String) -> T? {
         if let attr = self.attribute(by: attr) {
-            return try? T.deserialize(attr)
+            let deserialized = try? T.deserialize(attr)
+            if deserialized != nil { try? deserialized?.validate() }
+            return deserialized
         } else {
             return nil
         }
@@ -441,6 +603,41 @@ extension XMLElement {
     }
 }
 
+// MARK: String RawRepresentable
+
+/*: Provides XMLElement XMLAttributeDeserializable deserialization from String backed RawRepresentables
+    Added by [PeeJWeeJ](https://github.com/PeeJWeeJ) */
+public extension XMLElement {
+    /**
+     Attempts to deserialize the specified attribute of the current XMLElement to `T`
+     using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - throws: an XMLDeserializationError if there is a problem with deserialization
+     - returns: The deserialized `T` value
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A)  throws -> T where A.RawValue == String {
+        try value(ofAttribute: attr.rawValue)
+    }
+
+    /**
+     Attempts to deserialize the specified attribute of the current XMLElement to `T?`
+     using a String backed RawRepresentable (E.g. `String` backed `enum` cases)
+
+     - Note:
+     Convenience for value(ofAttribute: String)
+
+     - parameter attr: The attribute to deserialize
+     - returns: The deserialized `T?` value, or nil if the attribute does not exist.
+     */
+    func value<T: XMLAttributeDeserializable, A: RawRepresentable>(ofAttribute attr: A) -> T? where A.RawValue == String {
+        value(ofAttribute: attr.rawValue)
+    }
+}
+
 // MARK: - XMLDeserializationError
 
 /// The error that is thrown if there is a problem with deserialization
@@ -484,11 +681,11 @@ public enum XMLDeserializationError: Error, CustomStringConvertible {
             return "This node is invalid: \(node)"
         case .nodeHasNoValue:
             return "This node is empty"
-        case .typeConversionFailed(let type, let node):
+        case let .typeConversionFailed(type, node):
             return "Can't convert node \(node) to value of type \(type)"
-        case .attributeDoesNotExist(let element, let attribute):
+        case let .attributeDoesNotExist(element, attribute):
             return "Element \(element) does not contain attribute: \(attribute)"
-        case .attributeDeserializationFailed(let type, let attribute):
+        case let .attributeDeserializationFailed(type, attribute):
             return "Can't convert attribute \(attribute) to value of type \(type)"
         }
     }
@@ -506,7 +703,7 @@ extension String: XMLElementDeserializable, XMLAttributeDeserializable {
     - returns: the deserialized String value
     */
     public static func deserialize(_ element: XMLElement) -> String {
-        return element.text
+        element.text
     }
 
     /**
@@ -516,8 +713,10 @@ extension String: XMLElementDeserializable, XMLAttributeDeserializable {
      - returns: the deserialized String value
      */
     public static func deserialize(_ attribute: XMLAttribute) -> String {
-        return attribute.text
+        attribute.text
     }
+
+    public func validate() {}
 }
 
 extension Int: XMLElementDeserializable, XMLAttributeDeserializable {
@@ -551,6 +750,8 @@ extension Int: XMLElementDeserializable, XMLAttributeDeserializable {
         }
         return value
     }
+
+    public func validate() {}
 }
 
 extension Double: XMLElementDeserializable, XMLAttributeDeserializable {
@@ -584,6 +785,8 @@ extension Double: XMLElementDeserializable, XMLAttributeDeserializable {
         }
         return value
     }
+
+    public func validate() {}
 }
 
 extension Float: XMLElementDeserializable, XMLAttributeDeserializable {
@@ -617,6 +820,8 @@ extension Float: XMLElementDeserializable, XMLAttributeDeserializable {
         }
         return value
     }
+
+    public func validate() {}
 }
 
 extension Bool: XMLElementDeserializable, XMLAttributeDeserializable {
@@ -649,4 +854,6 @@ extension Bool: XMLElementDeserializable, XMLAttributeDeserializable {
         return value
     }
     // swiftlint:enable line_length
+
+    public func validate() {}
 }
diff --git a/Dependencies/SWXMLHash/shim.swift b/Dependencies/SWXMLHash/shim.swift
new file mode 100644
index 00000000..f0ec8967
--- /dev/null
+++ b/Dependencies/SWXMLHash/shim.swift
@@ -0,0 +1,36 @@
+//
+//  shim.swift
+//  SWXMLHash
+//
+//  Copyright (c) 2018 David Mohundro
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+//
+
+#if (!swift(>=4.1) && swift(>=4.0)) || !swift(>=3.3)
+
+    extension Sequence {
+        func compactMap<ElementOfResult>(
+            _ transform: (Self.Element
+            ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
+            try flatMap(transform)
+        }
+    }
+
+#endif
diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj
index 7507a2b0..c99329d5 100644
--- a/Macaw.xcodeproj/project.pbxproj
+++ b/Macaw.xcodeproj/project.pbxproj
@@ -147,15 +147,12 @@
 		5713C4F51E5AE2C300BBA4D9 /* CombineAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4F41E5AE2C300BBA4D9 /* CombineAnimationTests.swift */; };
 		5713C4F71E5C34C700BBA4D9 /* SequenceAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4F61E5C34C700BBA4D9 /* SequenceAnimationTests.swift */; };
 		5713C4F91E5C3FEE00BBA4D9 /* DelayedAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4F81E5C3FEE00BBA4D9 /* DelayedAnimationTests.swift */; };
-		572CEFC71E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */; };
-		572CEFC81E2CED4B008C7C83 /* SWXMLHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC61E2CED4B008C7C83 /* SWXMLHash.swift */; };
 		57614AFD1F83D15600875933 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1381E3B393900D1CB28 /* Group.swift */; };
 		57614AFE1F83D15600875933 /* TextRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1441E3B393900D1CB28 /* TextRenderer.swift */; };
 		57614AFF1F83D15600875933 /* CGFloat+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14D1E3B393900D1CB28 /* CGFloat+Double.swift */; };
 		57614B021F83D15600875933 /* RoundRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1341E3B393900D1CB28 /* RoundRect.swift */; };
 		57614B031F83D15600875933 /* UIImage2Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57900FF81EA0DEBF00809FFB /* UIImage2Image.swift */; };
 		57614B041F83D15600875933 /* SVGParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1471E3B393900D1CB28 /* SVGParser.swift */; };
-		57614B051F83D15600875933 /* SWXMLHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC61E2CED4B008C7C83 /* SWXMLHash.swift */; };
 		57614B071F83D15600875933 /* RenderUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1421E3B393900D1CB28 /* RenderUtils.swift */; };
 		57614B081F83D15600875933 /* FuncBounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0F11E3B393900D1CB28 /* FuncBounds.swift */; };
 		57614B0A1F83D15600875933 /* DoubleInterpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0EC1E3B393900D1CB28 /* DoubleInterpolation.swift */; };
@@ -234,7 +231,6 @@
 		57614B671F83D15600875933 /* ContentsInterpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A27BCE1E44C4EC0057BD3A /* ContentsInterpolation.swift */; };
 		57614B681F83D15600875933 /* GroupRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E13E1E3B393900D1CB28 /* GroupRenderer.swift */; };
 		57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14E1E3B393900D1CB28 /* NSTimer+Closure.swift */; };
-		57614B6C1F83D15600875933 /* SWXMLHash+TypeConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */; };
 		57614B6D1F83D15600875933 /* AnimationSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FF1E3B393900D1CB28 /* AnimationSequence.swift */; };
 		57614B6E1F83D15600875933 /* MorphingGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */; };
 		57614B6F1F83D15600875933 /* SVGConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1461E3B393900D1CB28 /* SVGConstants.swift */; };
@@ -681,6 +677,12 @@
 		A7B47E44230EA402009DD7E5 /* Graphics_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD451F45C28700966E06 /* Graphics_iOS.swift */; };
 		A7B47E45230EA404009DD7E5 /* Common_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD431F45C28200966E06 /* Common_iOS.swift */; };
 		A7E675561EC4213500BD9ECB /* NodeBoundsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */; };
+		A7F46DE0270B0EE8008DA1DF /* XMLHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDD270B0EE8008DA1DF /* XMLHash.swift */; };
+		A7F46DE1270B0EE8008DA1DF /* XMLHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDD270B0EE8008DA1DF /* XMLHash.swift */; };
+		A7F46DE2270B0EE8008DA1DF /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDE270B0EE8008DA1DF /* shim.swift */; };
+		A7F46DE3270B0EE8008DA1DF /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDE270B0EE8008DA1DF /* shim.swift */; };
+		A7F46DE4270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDF270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift */; };
+		A7F46DE5270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F46DDF270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift */; };
 		C410148E1F834D290022EE44 /* style.svg in Resources */ = {isa = PBXBuildFile; fileRef = C410148D1F834D280022EE44 /* style.svg */; };
 		C4153A8F1F8793DE001BA5EE /* small-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = C4153A8E1F8793DD001BA5EE /* small-logo.png */; };
 		C46E83551F94B20E00208037 /* transform.svg in Resources */ = {isa = PBXBuildFile; fileRef = C46E83541F94B20E00208037 /* transform.svg */; };
@@ -859,8 +861,6 @@
 		5713C4F41E5AE2C300BBA4D9 /* CombineAnimationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineAnimationTests.swift; sourceTree = "<group>"; };
 		5713C4F61E5C34C700BBA4D9 /* SequenceAnimationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SequenceAnimationTests.swift; sourceTree = "<group>"; };
 		5713C4F81E5C3FEE00BBA4D9 /* DelayedAnimationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelayedAnimationTests.swift; sourceTree = "<group>"; };
-		572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SWXMLHash+TypeConversion.swift"; sourceTree = "<group>"; };
-		572CEFC61E2CED4B008C7C83 /* SWXMLHash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWXMLHash.swift; sourceTree = "<group>"; };
 		57614B791F83D15600875933 /* MacawOSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MacawOSX.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		57614BD91F8739EE00875933 /* MacawView+PDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MacawView+PDF.swift"; sourceTree = "<group>"; };
 		57900FF81EA0DEBF00809FFB /* UIImage2Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImage2Image.swift; sourceTree = "<group>"; };
@@ -1260,6 +1260,9 @@
 		A74C832B229FB7690085A832 /* color-prop-04-t-manual-osx.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.svg"; sourceTree = "<group>"; };
 		A74C832D229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.reference"; sourceTree = "<group>"; };
 		A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NodeBoundsTests.swift; path = Bounds/NodeBoundsTests.swift; sourceTree = "<group>"; };
+		A7F46DDD270B0EE8008DA1DF /* XMLHash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLHash.swift; sourceTree = "<group>"; };
+		A7F46DDE270B0EE8008DA1DF /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = "<group>"; };
+		A7F46DDF270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = "<group>"; };
 		C410148D1F834D280022EE44 /* style.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = style.svg; sourceTree = "<group>"; };
 		C4153A8E1F8793DD001BA5EE /* small-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-logo.png"; sourceTree = "<group>"; };
 		C43B064C1F9738EF00787A35 /* clip.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = clip.svg; sourceTree = "<group>"; };
@@ -1448,8 +1451,9 @@
 		572CEFC41E2CED4B008C7C83 /* SWXMLHash */ = {
 			isa = PBXGroup;
 			children = (
-				572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */,
-				572CEFC61E2CED4B008C7C83 /* SWXMLHash.swift */,
+				A7F46DDE270B0EE8008DA1DF /* shim.swift */,
+				A7F46DDD270B0EE8008DA1DF /* XMLHash.swift */,
+				A7F46DDF270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift */,
 			);
 			path = SWXMLHash;
 			sourceTree = "<group>";
@@ -2697,7 +2701,6 @@
 				5B6E194020AC58F900454E7E /* Stroke.swift in Sources */,
 				57614B041F83D15600875933 /* SVGParser.swift in Sources */,
 				A7B47E42230EA3FC009DD7E5 /* MDisplayLink_iOS.swift in Sources */,
-				57614B051F83D15600875933 /* SWXMLHash.swift in Sources */,
 				57614B071F83D15600875933 /* RenderUtils.swift in Sources */,
 				57614B081F83D15600875933 /* FuncBounds.swift in Sources */,
 				57614B0A1F83D15600875933 /* DoubleInterpolation.swift in Sources */,
@@ -2738,6 +2741,7 @@
 				5B6E193020AC58F900454E7E /* LinearGradient.swift in Sources */,
 				57614B261F83D15600875933 /* ShapeAnimation.swift in Sources */,
 				57614B271F83D15600875933 /* TransformInterpolation.swift in Sources */,
+				A7F46DE1270B0EE8008DA1DF /* XMLHash.swift in Sources */,
 				57614B281F83D15600875933 /* ShapeAnimationGenerator.swift in Sources */,
 				57614B291F83D15600875933 /* AnimationUtils.swift in Sources */,
 				57614B2A1F83D15600875933 /* Polygon.swift in Sources */,
@@ -2776,6 +2780,7 @@
 				30FF496D215CF27E00FF653C /* MCAShapeLayerLineCap_macOS.swift in Sources */,
 				57614B491F83D15600875933 /* MView_macOS.swift in Sources */,
 				A7B47E3F230EA3F5009DD7E5 /* MCAShapeLayerLineCap_iOS.swift in Sources */,
+				A7F46DE3270B0EE8008DA1DF /* shim.swift in Sources */,
 				5B6E192E20AC58F900454E7E /* Font.swift in Sources */,
 				5BFEF5D120B80A83008DAC11 /* ColorMatrixEffect.swift in Sources */,
 				57614B4A1F83D15600875933 /* Easing.swift in Sources */,
@@ -2810,6 +2815,7 @@
 				5B9B970D2486506C00CAF2CE /* PathAnimationGenerator.swift in Sources */,
 				57614B631F83D15600875933 /* Insets.swift in Sources */,
 				3081E77E20DB58B100640F96 /* DescriptionExtensions.swift in Sources */,
+				A7F46DE5270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */,
 				57614B641F83D15600875933 /* Rect.swift in Sources */,
 				30FF4971215CF4CE00FF653C /* MCAMediaTimingFunctionName_macOS.swift in Sources */,
 				57614B651F83D15600875933 /* PathBuilder.swift in Sources */,
@@ -2820,7 +2826,6 @@
 				5B6E192A20AC58F900454E7E /* Align.swift in Sources */,
 				57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */,
 				5B6E193620AC58F900454E7E /* Stop.swift in Sources */,
-				57614B6C1F83D15600875933 /* SWXMLHash+TypeConversion.swift in Sources */,
 				57614B6D1F83D15600875933 /* AnimationSequence.swift in Sources */,
 				5B1A8C7720A15F7300E5FFAE /* SVGNodeLayout.swift in Sources */,
 				57614B6E1F83D15600875933 /* MorphingGenerator.swift in Sources */,
@@ -2842,7 +2847,6 @@
 				57900FF91EA0DEBF00809FFB /* UIImage2Image.swift in Sources */,
 				57E5E1AB1E3B393900D1CB28 /* SVGParser.swift in Sources */,
 				5B6E193F20AC58F900454E7E /* Stroke.swift in Sources */,
-				572CEFC81E2CED4B008C7C83 /* SWXMLHash.swift in Sources */,
 				57E5E1A71E3B393900D1CB28 /* RenderUtils.swift in Sources */,
 				57E5E1601E3B393900D1CB28 /* FuncBounds.swift in Sources */,
 				A718CD481F45C28700966E06 /* MView_iOS.swift in Sources */,
@@ -2884,6 +2888,7 @@
 				57A27BD31E44C5570057BD3A /* ShapeAnimationGenerator.swift in Sources */,
 				57E5E1571E3B393900D1CB28 /* AnimationUtils.swift in Sources */,
 				30FF496F215CF3B000FF653C /* MCAMediaTimingFunctionName_iOS.swift in Sources */,
+				A7F46DE0270B0EE8008DA1DF /* XMLHash.swift in Sources */,
 				57E5E1981E3B393900D1CB28 /* Polygon.swift in Sources */,
 				57E5E1701E3B393900D1CB28 /* TransformAnimation.swift in Sources */,
 				57E5E16C1E3B393900D1CB28 /* CombineAnimation.swift in Sources */,
@@ -2922,6 +2927,7 @@
 				5BFEF5CE20B80A83008DAC11 /* BlendEffect.swift in Sources */,
 				A718CD501F45C28F00966E06 /* MView_macOS.swift in Sources */,
 				57E5E1581E3B393900D1CB28 /* Easing.swift in Sources */,
+				A7F46DE2270B0EE8008DA1DF /* shim.swift in Sources */,
 				5BFEF5D020B80A83008DAC11 /* ColorMatrixEffect.swift in Sources */,
 				5B6E192D20AC58F900454E7E /* Font.swift in Sources */,
 				57E5E1971E3B393900D1CB28 /* Point.swift in Sources */,
@@ -2956,6 +2962,7 @@
 				3081E77D20DB58B100640F96 /* DescriptionExtensions.swift in Sources */,
 				57E5E19A1E3B393900D1CB28 /* Rect.swift in Sources */,
 				57E5E1941E3B393900D1CB28 /* PathBuilder.swift in Sources */,
+				A7F46DE4270B0EE8008DA1DF /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */,
 				57E5E1761E3B393900D1CB28 /* PinchEvent.swift in Sources */,
 				57A27BCF1E44C4EC0057BD3A /* ContentsInterpolation.swift in Sources */,
 				57E5E1A31E3B393900D1CB28 /* GroupRenderer.swift in Sources */,
@@ -2964,7 +2971,6 @@
 				57E5E1B11E3B393900D1CB28 /* NSTimer+Closure.swift in Sources */,
 				30FF4962215CE97300FF653C /* MCAMediaTimingFillMode_iOS.swift in Sources */,
 				5B6E193520AC58F900454E7E /* Stop.swift in Sources */,
-				572CEFC71E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift in Sources */,
 				30FF496B215CF0ED00FF653C /* MCAShapeLayerLineCap_iOS.swift in Sources */,
 				5BECDC7B222FE6DE009C8E6A /* PathAnimation.swift in Sources */,
 				57E5E16B1E3B393900D1CB28 /* AnimationSequence.swift in Sources */,
diff --git a/Source/svg/CSSParser.swift b/Source/svg/CSSParser.swift
index e919abcc..d22f0092 100644
--- a/Source/svg/CSSParser.swift
+++ b/Source/svg/CSSParser.swift
@@ -69,7 +69,7 @@ class CSSParser {
         return .byTag(text)
     }
 
-    func getStyles(element: SWXMLHash.XMLElement) -> [String: String] {
+	func getStyles(element: XMLHash.XMLElement) -> [String: String] {
         var styleAttributes = [String: String]()
 
         if let styles = stylesByTag[element.name] {
diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift
index 20c26c01..cbecf21a 100644
--- a/Source/svg/SVGParser.swift
+++ b/Source/svg/SVGParser.swift
@@ -133,12 +133,12 @@ open class SVGParser {
     }
 
     fileprivate func parse() throws -> Group {
-        let config = SWXMLHash.config { config in
+        let config = XMLHash.config { config in
             config.shouldProcessNamespaces = true
         }
         let parsedXml = config.parse(xmlString)
 
-        var svgElement: SWXMLHash.XMLElement?
+		var svgElement: XMLHash.XMLElement?
         for child in parsedXml.children {
             if let element = child.element {
                 if element.name == "svg" {
@@ -203,7 +203,7 @@ open class SVGParser {
         }
     }
 
-    fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) throws -> SVGNodeLayout? {
+	fileprivate func parseViewBox(_ element: XMLHash.XMLElement) throws -> SVGNodeLayout? {
         let widthAttributeNil = element.allAttributes["width"] == nil
         let heightAttributeNil = element.allAttributes["height"] == nil
         let viewBoxAttributeNil = element.allAttributes["viewBox"] == nil
@@ -508,7 +508,7 @@ open class SVGParser {
         return Group(contents: groupNodes, place: getPosition(element), tag: getTag(element))
     }
 
-    fileprivate func getPosition(_ element: SWXMLHash.XMLElement) -> Transform {
+	fileprivate func getPosition(_ element: XMLHash.XMLElement) -> Transform {
         guard let transformAttribute = element.allAttributes["transform"]?.text else {
             return Transform.identity
         }
@@ -666,7 +666,7 @@ open class SVGParser {
     }
 
     fileprivate func getStyleAttributes(_ groupAttributes: [String: String],
-                                        element: SWXMLHash.XMLElement) -> [String: String] {
+										element: XMLHash.XMLElement) -> [String: String] {
         var styleAttributes: [String: String] = groupAttributes
 
         for (att, val) in styles.getStyles(element: element) {
@@ -876,7 +876,7 @@ open class SVGParser {
         return dashes
     }
 
-    fileprivate func getMatrix(_ element: SWXMLHash.XMLElement, attribute: String) -> [Double] {
+	fileprivate func getMatrix(_ element: XMLHash.XMLElement, attribute: String) -> [Double] {
         var result = [Double]()
         if let values = element.allAttributes[attribute]?.text {
             let separatedValues = values.components(separatedBy: CharacterSet(charactersIn: " ,"))
@@ -896,7 +896,7 @@ open class SVGParser {
         return 0
     }
 
-    fileprivate func getTag(_ element: SWXMLHash.XMLElement) -> [String] {
+	fileprivate func getTag(_ element: XMLHash.XMLElement) -> [String] {
         let id = element.allAttributes["id"]?.text
         return id.map { [$0] } ?? []
     }
@@ -1081,7 +1081,7 @@ open class SVGParser {
         return Align.min
     }
 
-    fileprivate func parseSimpleText(_ text: SWXMLHash.XMLElement,
+	fileprivate func parseSimpleText(_ text: XMLHash.XMLElement,
                                      textAnchor: String?,
                                      fill: Fill?,
                                      stroke: Stroke?,
@@ -1156,7 +1156,7 @@ open class SVGParser {
                             baseline: .alphabetic,
                             place: place,
                             opacity: opacity)
-            } else if let tspanElement = element as? SWXMLHash.XMLElement,
+			} else if let tspanElement = element as? XMLHash.XMLElement,
                       tspanElement.name == "tspan" {
                 // parse as <tspan> element
                 // ultimately skip it if it cannot be parsed
@@ -1187,7 +1187,7 @@ open class SVGParser {
         return collection
     }
 
-    fileprivate func parseTspan(_ element: SWXMLHash.XMLElement,
+	fileprivate func parseTspan(_ element: XMLHash.XMLElement,
                                 withWhitespace: Bool = false,
                                 textAnchor: String?,
                                 fill: Fill?,
@@ -1242,7 +1242,7 @@ open class SVGParser {
             weight: getFontWeight(attributes) ?? fontWeight ?? "normal")
     }
 
-    fileprivate func getTspanPosition(_ element: SWXMLHash.XMLElement,
+	fileprivate func getTspanPosition(_ element: XMLHash.XMLElement,
                                       bounds: Rect,
                                       previousCollectedTspan: Node?,
                                       withWhitespace: inout Bool) -> Transform {
@@ -1653,14 +1653,14 @@ open class SVGParser {
         return .none
     }
 
-    fileprivate func getDoubleValue(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? {
+	fileprivate func getDoubleValue(_ element: XMLHash.XMLElement, attribute: String) -> Double? {
         guard let attributeValue = element.allAttributes[attribute]?.text else {
             return .none
         }
         return doubleFromString(attributeValue)
     }
 
-    fileprivate func getDimensionValue(_ element: SWXMLHash.XMLElement, attribute: String) -> SVGLength? {
+	fileprivate func getDimensionValue(_ element: XMLHash.XMLElement, attribute: String) -> SVGLength? {
         guard let attributeValue = element.allAttributes[attribute]?.text else {
             return .none
         }
@@ -1701,7 +1701,7 @@ open class SVGParser {
         }
     }
 
-    fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? {
+	fileprivate func getDoubleValueFromPercentage(_ element: XMLHash.XMLElement, attribute: String) -> Double? {
         guard let attributeValue = element.allAttributes[attribute]?.text else {
             return .none
         }
@@ -1716,7 +1716,7 @@ open class SVGParser {
         return .none
     }
 
-    fileprivate func getIntValue(_ element: SWXMLHash.XMLElement, attribute: String) -> Int? {
+	fileprivate func getIntValue(_ element: XMLHash.XMLElement, attribute: String) -> Int? {
         if let attributeValue = element.allAttributes[attribute]?.text {
             if let doubleValue = Double(attributeValue) {
                 return Int(doubleValue)