1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-07-14 16:30:34 +03:00

Merge pull request #772 from exyte/769-SWXMLHash-Compatibility

#769 SWXMLHash Compatibility
This commit is contained in:
Yuri Strot 2021-10-08 16:43:17 +07:00 committed by GitHub
commit b332010acd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 597 additions and 156 deletions

View File

@ -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)
}
}

View File

@ -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() {}
}

36
Dependencies/SWXMLHash/shim.swift vendored Normal file
View File

@ -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

View File

@ -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 */,

View File

@ -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] {

View File

@ -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)