From d3c5a4e38085ba8dd0ac4592a282d4279a6eb612 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Thu, 28 Apr 2022 14:27:58 -0500 Subject: [PATCH 01/16] feat: improve nodeIsInvalid error messages --- .../XMLIndexer+XMLIndexerDeserializable.swift | 31 ++++++++++++++----- .../TypeConversionBasicTypesTests.swift | 21 +++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index 2ddb8f31..a4f06bad 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -142,8 +142,10 @@ public extension XMLIndexer { return try element.value(ofAttribute: attr) case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLAttribute \(attr) -> T") } } @@ -181,8 +183,10 @@ public extension XMLIndexer { return try [element].map { try $0.value(ofAttribute: attr) } case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLAttribute \(attr) -> [T]") } } @@ -223,8 +227,10 @@ public extension XMLIndexer { return [element].map { $0.value(ofAttribute: attr) } case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLAttribute \(attr) -> [T?]") } } @@ -244,8 +250,10 @@ public extension XMLIndexer { return deserialized case .stream(let opStream): return try opStream.findElements().value() + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLElement -> T") } } @@ -365,8 +373,10 @@ public extension XMLIndexer { return deserialized case .stream(let opStream): return try opStream.findElements().value() + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLIndexer -> T") } } @@ -411,8 +421,10 @@ public extension XMLIndexer { } case .stream(let opStream): return try opStream.findElements().value() + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLIndexer -> [T]") } } @@ -465,8 +477,11 @@ public extension XMLIndexer { } case .stream(let opStream): return try opStream.findElements().value() + + case .xmlError(let indexingError): + throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: - throw XMLDeserializationError.nodeIsInvalid(node: self) + throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLIndexer -> [T?]") } } } @@ -643,7 +658,7 @@ public extension XMLElement { /// The error that is thrown if there is a problem with deserialization public enum XMLDeserializationError: Error, CustomStringConvertible { case implementationIsMissing(method: String) - case nodeIsInvalid(node: XMLIndexer) + case nodeIsInvalid(node: String) case nodeHasNoValue case typeConversionFailed(type: String, element: XMLElement) case attributeDoesNotExist(element: XMLElement, attribute: String) diff --git a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift index b701b356..dd10097d 100644 --- a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift @@ -99,6 +99,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as String)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -149,6 +150,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testShouldThrowWhenConvertingMissingAttributeToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["attr"].value(ofAttribute: "missing") as String)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -186,6 +188,7 @@ class TypeConversionBasicTypesTests: XCTestCase { case missing } XCTAssertThrowsError(try (parser!["root"]["attr"].value(ofAttribute: Keys.missing) as String)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -212,6 +215,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testIntShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Int)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -221,6 +225,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testIntShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as Int)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -239,6 +244,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testIntShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Int?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -280,6 +286,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testDoubleShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Double)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -289,6 +296,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testDoubleShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as Double)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -307,6 +315,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testDoubleShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Double?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -348,6 +357,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testFloatShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Float)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -357,6 +367,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testFloatShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as Float)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -375,6 +386,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testFloatShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Float?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -418,6 +430,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBoolShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Bool)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -427,6 +440,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBoolShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as Bool)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -447,6 +461,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBoolShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as Bool?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -490,6 +505,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBasicItemShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as BasicItem)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -499,6 +515,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBasicItemShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as BasicItem)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -517,6 +534,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testBasicItemShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as BasicItem?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -556,6 +574,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testAttributeItemShouldThrowWhenConvertingEmptyToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as AttributeItem)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -565,6 +584,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testAttributeItemShouldThrowWhenConvertingMissingToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["missing"].value() as AttributeItem)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return @@ -583,6 +603,7 @@ class TypeConversionBasicTypesTests: XCTestCase { func testAttributeItemShouldConvertEmptyToOptional() { XCTAssertThrowsError(try (parser!["root"]["empty"].value() as AttributeItem?)) { error in + print(error) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return From ddc35fea2f85edd6216cae626c7eff2a6012971d Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Fri, 29 Apr 2022 08:49:35 -0500 Subject: [PATCH 02/16] fix: bump the iOS version for build --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9fc07c9e..88461295 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: run: | set -o pipefail xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash OSX" | xcpretty - xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash iOS" -sdk iphonesimulator -destination "OS=15.0,name=iPhone 12" | xcpretty + xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash iOS" -sdk iphonesimulator -destination "OS=15.2,name=iPhone 13" | xcpretty xcodebuild $ACTION $WORKSPACE -scheme "SWXMLHash tvOS" -sdk appletvsimulator -destination "name=Apple TV" | xcpretty xcodebuild build $WORKSPACE -scheme "SWXMLHash watchOS" -sdk watchsimulator | xcpretty bash <(curl -s https://codecov.io/bash) -t ${{secrets.CODECOV_TOKEN}} From 2d90bc771bc10094e7236f1563d47fcd13f6107a Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Fri, 29 Apr 2022 09:06:28 -0500 Subject: [PATCH 03/16] fix: swiftlint whitespace issue --- Source/XMLIndexer+XMLIndexerDeserializable.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index a4f06bad..aecd7fa7 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -477,7 +477,6 @@ public extension XMLIndexer { } case .stream(let opStream): return try opStream.findElements().value() - case .xmlError(let indexingError): throw XMLDeserializationError.nodeIsInvalid(node: indexingError.description) default: From 4d6f845139a829b6f299a14a1e11af05c1cb7851 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 09:40:42 -0500 Subject: [PATCH 04/16] refactor: XMLDeserializationError to own file --- SWXMLHash.xcodeproj/project.pbxproj | 10 ++++ Source/XMLDeserializationError.swift | 59 +++++++++++++++++++ .../XMLIndexer+XMLIndexerDeserializable.swift | 52 ---------------- 3 files changed, 69 insertions(+), 52 deletions(-) create mode 100644 Source/XMLDeserializationError.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index fd485542..6ae726ae 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; + 6317F1A12821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; + 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; + 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; + 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; 6C42BED1205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED2205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED3205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; @@ -126,6 +130,7 @@ /* Begin PBXFileReference section */ 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = ""; }; + 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLDeserializationError.swift; sourceTree = ""; }; 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; 6C42BECC2051834B00137D31 /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; 6C42BED4205183AF00137D31 /* shim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; @@ -250,6 +255,7 @@ CD6083F4196CA106000B4F8D /* SWXMLHash.h */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, + 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, ); path = Source; sourceTree = ""; @@ -605,6 +611,7 @@ files = ( 6C42BED8205183B000137D31 /* shim.swift in Sources */, CD60840C196CA11D000B4F8D /* XMLHash.swift in Sources */, + 6317F1A12821756400F6C364 /* XMLDeserializationError.swift in Sources */, 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -635,6 +642,7 @@ files = ( 6C42BED5205183B000137D31 /* shim.swift in Sources */, CD7934C61A7581F500867857 /* XMLHash.swift in Sources */, + 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */, 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -665,6 +673,7 @@ files = ( 6C42BED6205183B000137D31 /* shim.swift in Sources */, CDEA72731C00B0D900C10B28 /* XMLHash.swift in Sources */, + 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */, 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -695,6 +704,7 @@ files = ( 6C42BED7205183B000137D31 /* shim.swift in Sources */, CDEA72741C00B0E300C10B28 /* XMLHash.swift in Sources */, + 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */, 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/XMLDeserializationError.swift b/Source/XMLDeserializationError.swift new file mode 100644 index 00000000..99b7b63f --- /dev/null +++ b/Source/XMLDeserializationError.swift @@ -0,0 +1,59 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Created by David Mohundro on 5/3/22. +// + +import Foundation + +public enum XMLDeserializationError: Error, CustomStringConvertible { + case implementationIsMissing(method: String) + case nodeIsInvalid(node: String) + case nodeHasNoValue + case typeConversionFailed(type: String, element: XMLElement) + case attributeDoesNotExist(element: XMLElement, attribute: String) + case attributeDeserializationFailed(type: String, attribute: XMLAttribute) + +// swiftlint:disable identifier_name + @available(*, unavailable, renamed: "implementationIsMissing(method:)") + public static func ImplementationIsMissing(method: String) -> XMLDeserializationError { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "nodeHasNoValue(_:)") + public static func NodeHasNoValue(_: IndexOps) -> XMLDeserializationError { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "typeConversionFailed(_:)") + public static func TypeConversionFailed(_: IndexingError) -> XMLDeserializationError { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "attributeDoesNotExist(_:_:)") + public static func AttributeDoesNotExist(_ attr: String, _ value: String) throws -> XMLDeserializationError { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "attributeDeserializationFailed(_:_:)") + public static func AttributeDeserializationFailed(_ attr: String, _ value: String) throws -> XMLDeserializationError { + fatalError("unavailable") + } +// swiftlint:enable identifier_name + + /// The text description for the error thrown + public var description: String { + switch self { + case .implementationIsMissing(let method): + return "This deserialization method is not implemented: \(method)" + case .nodeIsInvalid(let node): + return "This node is invalid: \(node)" + case .nodeHasNoValue: + return "This node is empty" + case let .typeConversionFailed(type, node): + return "Can't convert node \(node) to value of type \(type)" + case let .attributeDoesNotExist(element, attribute): + return "Element \(element) does not contain attribute: \(attribute)" + case let .attributeDeserializationFailed(type, attribute): + return "Can't convert attribute \(attribute) to value of type \(type)" + } + } +} + diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index aecd7fa7..adef2c18 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -652,59 +652,7 @@ public extension XMLElement { } } -// MARK: - XMLDeserializationError - /// The error that is thrown if there is a problem with deserialization -public enum XMLDeserializationError: Error, CustomStringConvertible { - case implementationIsMissing(method: String) - case nodeIsInvalid(node: String) - case nodeHasNoValue - case typeConversionFailed(type: String, element: XMLElement) - case attributeDoesNotExist(element: XMLElement, attribute: String) - case attributeDeserializationFailed(type: String, attribute: XMLAttribute) - -// swiftlint:disable identifier_name - @available(*, unavailable, renamed: "implementationIsMissing(method:)") - public static func ImplementationIsMissing(method: String) -> XMLDeserializationError { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "nodeHasNoValue(_:)") - public static func NodeHasNoValue(_: IndexOps) -> XMLDeserializationError { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "typeConversionFailed(_:)") - public static func TypeConversionFailed(_: IndexingError) -> XMLDeserializationError { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "attributeDoesNotExist(_:_:)") - public static func AttributeDoesNotExist(_ attr: String, _ value: String) throws -> XMLDeserializationError { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "attributeDeserializationFailed(_:_:)") - public static func AttributeDeserializationFailed(_ attr: String, _ value: String) throws -> XMLDeserializationError { - fatalError("unavailable") - } -// swiftlint:enable identifier_name - - /// The text description for the error thrown - public var description: String { - switch self { - case .implementationIsMissing(let method): - return "This deserialization method is not implemented: \(method)" - case .nodeIsInvalid(let node): - return "This node is invalid: \(node)" - case .nodeHasNoValue: - return "This node is empty" - case let .typeConversionFailed(type, node): - return "Can't convert node \(node) to value of type \(type)" - case let .attributeDoesNotExist(element, attribute): - return "Element \(element) does not contain attribute: \(attribute)" - case let .attributeDeserializationFailed(type, attribute): - return "Can't convert attribute \(attribute) to value of type \(type)" - } - } -} - // MARK: - Common types deserialization extension String: XMLElementDeserializable, XMLAttributeDeserializable { From cd81c77241e53c335abfb5a59725ce970228f99a Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 13:21:02 -0500 Subject: [PATCH 05/16] refactor: break up into multiple files This commit also introduces a new XMLDeserialization type that hopefully will make it easier to implement deserialization for custom types. --- SWXMLHash.xcodeproj/project.pbxproj | 92 +++++- Source/Bool+XMLDeserialization.swift | 60 ++++ Source/Double+XMLDeserialization.swift | 61 ++++ Source/Float+XMLDeserialization.swift | 61 ++++ Source/Int+XMLDeserialization.swift | 61 ++++ Source/String+XMLDeserialization.swift | 52 ++++ Source/XMLAttributeDeserializable.swift | 55 ++++ Source/XMLDeserialization.swift | 74 +++++ Source/XMLDeserializationError.swift | 24 +- Source/XMLElementDeserializable.swift | 56 ++++ .../XMLIndexer+XMLIndexerDeserializable.swift | 277 ------------------ Source/XMLIndexerDeserializable.swift | 56 ++++ 12 files changed, 649 insertions(+), 280 deletions(-) create mode 100644 Source/Bool+XMLDeserialization.swift create mode 100644 Source/Double+XMLDeserialization.swift create mode 100644 Source/Float+XMLDeserialization.swift create mode 100644 Source/Int+XMLDeserialization.swift create mode 100644 Source/String+XMLDeserialization.swift create mode 100644 Source/XMLAttributeDeserializable.swift create mode 100644 Source/XMLDeserialization.swift create mode 100644 Source/XMLElementDeserializable.swift create mode 100644 Source/XMLIndexerDeserializable.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index 6ae726ae..e3a1699d 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -15,6 +15,42 @@ 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; + 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; + 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; + 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; + 6317F1A9282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; + 63ED3C082821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; + 63ED3C092821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; + 63ED3C0A2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; + 63ED3C0B2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; + 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; + 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; + 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; + 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; + 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; + 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; + 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; + 63ED3C132821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; + 63ED3C142821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */; }; + 63ED3C152821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */; }; + 63ED3C162821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */; }; + 63ED3C172821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */; }; + 63ED3C182821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; + 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; + 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; + 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; + 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; + 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; + 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; + 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; + 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; + 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; + 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; + 63ED3C232821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; + 63ED3C242821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; + 63ED3C252821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; + 63ED3C262821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; + 63ED3C272821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; 6C42BED1205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED2205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED3205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; @@ -131,6 +167,15 @@ /* Begin PBXFileReference section */ 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = ""; }; 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLDeserializationError.swift; sourceTree = ""; }; + 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float+XMLDeserialization.swift"; sourceTree = ""; }; + 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+XMLDeserialization.swift"; sourceTree = ""; }; + 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLDeserialization.swift; sourceTree = ""; }; + 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLAttributeDeserializable.swift; sourceTree = ""; }; + 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+XMLDeserialization.swift"; sourceTree = ""; }; + 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLElementDeserializable.swift; sourceTree = ""; }; + 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLIndexerDeserializable.swift; sourceTree = ""; }; + 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+XMLDeserialization.swift"; sourceTree = ""; }; + 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+XMLDeserialization.swift"; sourceTree = ""; }; 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; 6C42BECC2051834B00137D31 /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; 6C42BED4205183AF00137D31 /* shim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; @@ -250,12 +295,21 @@ CD6083F1196CA106000B4F8D /* Source */ = { isa = PBXGroup; children = ( + 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */, + 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */, + 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */, + 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */, 6C42BED4205183AF00137D31 /* shim.swift */, + 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */, CD6083F2196CA106000B4F8D /* Supporting Files */, CD6083F4196CA106000B4F8D /* SWXMLHash.h */, + 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */, + 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */, + 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, + 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, - 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, + 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */, ); path = Source; sourceTree = ""; @@ -609,9 +663,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, + 63ED3C142821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED8205183B000137D31 /* shim.swift in Sources */, + 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C182821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD60840C196CA11D000B4F8D /* XMLHash.swift in Sources */, + 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */, 6317F1A12821756400F6C364 /* XMLDeserializationError.swift in Sources */, + 63ED3C082821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C242821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -640,9 +703,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, + 63ED3C152821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED5205183B000137D31 /* shim.swift in Sources */, + 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD7934C61A7581F500867857 /* XMLHash.swift in Sources */, + 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */, 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */, + 63ED3C092821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C252821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -671,9 +743,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, + 63ED3C162821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED6205183B000137D31 /* shim.swift in Sources */, + 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72731C00B0D900C10B28 /* XMLHash.swift in Sources */, + 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */, 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */, + 63ED3C0A2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C262821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -702,9 +783,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C132821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, + 63ED3C172821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C232821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED7205183B000137D31 /* shim.swift in Sources */, + 6317F1A9282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72741C00B0E300C10B28 /* XMLHash.swift in Sources */, + 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */, 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */, + 63ED3C0B2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C272821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/Bool+XMLDeserialization.swift b/Source/Bool+XMLDeserialization.swift new file mode 100644 index 00000000..37c71d3e --- /dev/null +++ b/Source/Bool+XMLDeserialization.swift @@ -0,0 +1,60 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +extension Bool: XMLDeserialization, XMLElementDeserializable { + // swiftlint:disable line_length + /** + Attempts to deserialize XML element content to a Bool. This uses NSString's 'boolValue' + described [here](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/#//apple_ref/occ/instp/NSString/boolValue) + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized + - returns: the deserialized Bool value + */ + public static func deserialize(_ element: XMLElement) throws -> Bool { + let value = Bool(NSString(string: try element.nonEmptyTextOrThrow()).boolValue) + return value + } + + /** + Attempts to deserialize XML attribute content to a Bool. This uses NSString's 'boolValue' + described [here](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/#//apple_ref/occ/instp/NSString/boolValue) + + - parameter attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be + deserialized + - returns: the deserialized Bool value + */ + public static func deserialize(_ attribute: XMLAttribute) throws -> Bool { + let value = Bool(NSString(string: attribute.text).boolValue) + return value + } + // swiftlint:enable line_length + + public func validate() {} +} diff --git a/Source/Double+XMLDeserialization.swift b/Source/Double+XMLDeserialization.swift new file mode 100644 index 00000000..c91c2d55 --- /dev/null +++ b/Source/Double+XMLDeserialization.swift @@ -0,0 +1,61 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +extension Double: XMLDeserialization, XMLElementDeserializable { + /** + Attempts to deserialize XML element content to a Double + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized + - returns: the deserialized Double value + */ + public static func deserialize(_ element: XMLElement) throws -> Double { + guard let value = Double(try element.nonEmptyTextOrThrow()) else { + throw XMLDeserializationError.typeConversionFailed(type: "Double", element: element) + } + return value + } + + /** + Attempts to deserialize XML attribute content to a Double + + - parameter attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be + deserialized + - returns: the deserialized Double value + */ + public static func deserialize(_ attribute: XMLAttribute) throws -> Double { + guard let value = Double(attribute.text) else { + throw XMLDeserializationError.attributeDeserializationFailed( + type: "Double", attribute: attribute) + } + return value + } + + public func validate() {} +} diff --git a/Source/Float+XMLDeserialization.swift b/Source/Float+XMLDeserialization.swift new file mode 100644 index 00000000..21e151d5 --- /dev/null +++ b/Source/Float+XMLDeserialization.swift @@ -0,0 +1,61 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +extension Float: XMLDeserialization { + /** + Attempts to deserialize XML element content to a Float + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized + - returns: the deserialized Float value + */ + public static func deserialize(_ element: XMLElement) throws -> Float { + guard let value = Float(try element.nonEmptyTextOrThrow()) else { + throw XMLDeserializationError.typeConversionFailed(type: "Float", element: element) + } + return value + } + + /** + Attempts to deserialize XML attribute content to a Float + + - parameter attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be + deserialized + - returns: the deserialized Float value + */ + public static func deserialize(_ attribute: XMLAttribute) throws -> Float { + guard let value = Float(attribute.text) else { + throw XMLDeserializationError.attributeDeserializationFailed( + type: "Float", attribute: attribute) + } + return value + } + + public func validate() {} +} diff --git a/Source/Int+XMLDeserialization.swift b/Source/Int+XMLDeserialization.swift new file mode 100644 index 00000000..9c004b9a --- /dev/null +++ b/Source/Int+XMLDeserialization.swift @@ -0,0 +1,61 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +extension Int: XMLDeserialization { + /** + Attempts to deserialize XML element content to a Int + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized + - returns: the deserialized Int value + */ + public static func deserialize(_ element: XMLElement) throws -> Int { + guard let value = Int(try element.nonEmptyTextOrThrow()) else { + throw XMLDeserializationError.typeConversionFailed(type: "Int", element: element) + } + return value + } + + /** + Attempts to deserialize XML attribute content to an Int + + - parameter attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be + deserialized + - returns: the deserialized Int value + */ + public static func deserialize(_ attribute: XMLAttribute) throws -> Int { + guard let value = Int(attribute.text) else { + throw XMLDeserializationError.attributeDeserializationFailed( + type: "Int", attribute: attribute) + } + return value + } + + public func validate() {} +} diff --git a/Source/String+XMLDeserialization.swift b/Source/String+XMLDeserialization.swift new file mode 100644 index 00000000..52b60aad --- /dev/null +++ b/Source/String+XMLDeserialization.swift @@ -0,0 +1,52 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +extension String: XMLDeserialization { + /** + Attempts to deserialize XML element content to a String + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized + - returns: the deserialized String value + */ + public static func deserialize(_ element: XMLElement) -> String { + element.text + } + + /** + Attempts to deserialize XML Attribute content to a String + + - parameter attribute: the XMLAttribute to be deserialized + - returns: the deserialized String value + */ + public static func deserialize(_ attribute: XMLAttribute) -> String { + attribute.text + } + + public func validate() {} +} diff --git a/Source/XMLAttributeDeserializable.swift b/Source/XMLAttributeDeserializable.swift new file mode 100644 index 00000000..5bfde355 --- /dev/null +++ b/Source/XMLAttributeDeserializable.swift @@ -0,0 +1,55 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +/// 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 +public extension XMLAttributeDeserializable { + /** + A default implementation that will throw an error if it is called + + - parameters: + - attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found + - returns: this won't ever return because of the error being thrown + */ + static func deserialize(attribute: XMLAttribute) throws -> Self { + throw XMLDeserializationError.implementationIsMissing( + method: "XMLAttributeDeserializable(element: XMLAttribute)") + } + /** + A default do nothing implementation of validation. + - throws: nothing + */ + func validate() throws {} +} diff --git a/Source/XMLDeserialization.swift b/Source/XMLDeserialization.swift new file mode 100644 index 00000000..5e3f850b --- /dev/null +++ b/Source/XMLDeserialization.swift @@ -0,0 +1,74 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +/// This protocol implements both XMLAttributeDeserializable and XMLElementDeserializable meaning +/// that it can be used to easily provide custom deserialization for your types. +public protocol XMLDeserialization: XMLAttributeDeserializable, XMLElementDeserializable { + /// Method for deserializing elements from XMLElement + static func deserialize(_ element: XMLElement) throws -> Self + + /// Method for deserializing elements from XMLAttribute + static func deserialize(_ attribute: XMLAttribute) throws -> Self + + /// Method for validating elements from XMLElement post deserialization + func validate() throws +} + +/// Provides XMLAttribute deserialization / type transformation support +public extension XMLDeserialization { + /** + A default implementation that will throw an error if it is called + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found + - returns: this won't ever return because of the error being thrown + */ + static func deserialize(_ element: XMLElement) throws -> Self { + throw XMLDeserializationError.implementationIsMissing( + method: "XMLElementDeserializable.deserialize(element: XMLElement)") + } + + /** + A default implementation that will throw an error if it is called + + - parameters: + - attribute: The XMLAttribute to be deserialized + - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found + - returns: this won't ever return because of the error being thrown + */ + static func deserialize(attribute: XMLAttribute) throws -> Self { + throw XMLDeserializationError.implementationIsMissing( + method: "XMLAttributeDeserializable(element: XMLAttribute)") + } + + /** + A default do nothing implementation of validation. + - throws: nothing + */ + func validate() throws {} +} diff --git a/Source/XMLDeserializationError.swift b/Source/XMLDeserializationError.swift index 99b7b63f..f40c1a01 100644 --- a/Source/XMLDeserializationError.swift +++ b/Source/XMLDeserializationError.swift @@ -2,11 +2,32 @@ // XMLDeserializationError.swift // SWXMLHash // -// Created by David Mohundro on 5/3/22. +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. // +// swiftlint:disable line_length + import Foundation +/// The error that is thrown if there is a problem with deserialization public enum XMLDeserializationError: Error, CustomStringConvertible { case implementationIsMissing(method: String) case nodeIsInvalid(node: String) @@ -56,4 +77,3 @@ public enum XMLDeserializationError: Error, CustomStringConvertible { } } } - diff --git a/Source/XMLElementDeserializable.swift b/Source/XMLElementDeserializable.swift new file mode 100644 index 00000000..8e1deda6 --- /dev/null +++ b/Source/XMLElementDeserializable.swift @@ -0,0 +1,56 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +/// Provides XMLElement deserialization / type transformation support +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 +public extension XMLElementDeserializable { + /** + A default implementation that will throw an error if it is called + + - parameters: + - element: the XMLElement to be deserialized + - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found + - returns: this won't ever return because of the error being thrown + */ + static func deserialize(_ element: XMLElement) throws -> Self { + throw XMLDeserializationError.implementationIsMissing( + method: "XMLElementDeserializable.deserialize(element: XMLElement)") + } + + /** + A default do nothing implementation of validation. + - throws: nothing + */ + func validate() throws {} +} diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index adef2c18..60e44607 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -28,106 +28,7 @@ import Foundation -// MARK: - XMLIndexerDeserializable - -/// Provides XMLIndexer deserialization / type transformation support -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 -public extension XMLIndexerDeserializable { - /** - A default implementation that will throw an error if it is called - - - parameters: - - element: the XMLIndexer to be deserialized - - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found - - returns: this won't ever return because of the error being thrown - */ - static func deserialize(_ element: XMLIndexer) throws -> Self { - throw XMLDeserializationError.implementationIsMissing( - method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)") - } - - /** - A default do nothing implementation of validation. - - throws: nothing - */ - func validate() throws {} -} - -// MARK: - XMLElementDeserializable - -/// Provides XMLElement deserialization / type transformation support -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 -public extension XMLElementDeserializable { - /** - A default implementation that will throw an error if it is called - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found - - returns: this won't ever return because of the error being thrown - */ - static func deserialize(_ element: XMLElement) throws -> Self { - 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 -public extension XMLAttributeDeserializable { - /** - A default implementation that will throw an error if it is called - - - parameters: - - attribute: The XMLAttribute to be deserialized - - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found - - returns: this won't ever return because of the error being thrown - */ - static func deserialize(attribute: XMLAttribute) throws -> Self { - 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 - /** Attempts to deserialize the value of the specified attribute of the current XMLIndexer element to `T` @@ -234,8 +135,6 @@ public extension XMLIndexer { } } - // MARK: - XMLElementDeserializable - /** Attempts to deserialize the current XMLElement element to `T` @@ -357,8 +256,6 @@ public extension XMLIndexer { } } - // MARK: - XMLIndexerDeserializable - /** Attempts to deserialize the current XMLIndexer element to `T` @@ -485,8 +382,6 @@ 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 { @@ -565,8 +460,6 @@ public extension XMLIndexer { } } -// MARK: - XMLElement Extensions - extension XMLElement { /** Attempts to deserialize the specified attribute of the current XMLElement to `T` @@ -617,8 +510,6 @@ extension XMLElement { } } -// MARK: String RawRepresentable - /*: Provides XMLElement XMLAttributeDeserializable deserialization from String backed RawRepresentables Added by [PeeJWeeJ](https://github.com/PeeJWeeJ) */ public extension XMLElement { @@ -651,171 +542,3 @@ public extension XMLElement { value(ofAttribute: attr.rawValue) } } - -/// The error that is thrown if there is a problem with deserialization -// MARK: - Common types deserialization - -extension String: XMLElementDeserializable, XMLAttributeDeserializable { - /** - Attempts to deserialize XML element content to a String - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized - - returns: the deserialized String value - */ - public static func deserialize(_ element: XMLElement) -> String { - element.text - } - - /** - Attempts to deserialize XML Attribute content to a String - - - parameter attribute: the XMLAttribute to be deserialized - - returns: the deserialized String value - */ - public static func deserialize(_ attribute: XMLAttribute) -> String { - attribute.text - } - - public func validate() {} -} - -extension Int: XMLElementDeserializable, XMLAttributeDeserializable { - /** - Attempts to deserialize XML element content to a Int - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized - - returns: the deserialized Int value - */ - public static func deserialize(_ element: XMLElement) throws -> Int { - guard let value = Int(try element.nonEmptyTextOrThrow()) else { - throw XMLDeserializationError.typeConversionFailed(type: "Int", element: element) - } - return value - } - - /** - Attempts to deserialize XML attribute content to an Int - - - parameter attribute: The XMLAttribute to be deserialized - - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be - deserialized - - returns: the deserialized Int value - */ - public static func deserialize(_ attribute: XMLAttribute) throws -> Int { - guard let value = Int(attribute.text) else { - throw XMLDeserializationError.attributeDeserializationFailed( - type: "Int", attribute: attribute) - } - return value - } - - public func validate() {} -} - -extension Double: XMLElementDeserializable, XMLAttributeDeserializable { - /** - Attempts to deserialize XML element content to a Double - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized - - returns: the deserialized Double value - */ - public static func deserialize(_ element: XMLElement) throws -> Double { - guard let value = Double(try element.nonEmptyTextOrThrow()) else { - throw XMLDeserializationError.typeConversionFailed(type: "Double", element: element) - } - return value - } - - /** - Attempts to deserialize XML attribute content to a Double - - - parameter attribute: The XMLAttribute to be deserialized - - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be - deserialized - - returns: the deserialized Double value - */ - public static func deserialize(_ attribute: XMLAttribute) throws -> Double { - guard let value = Double(attribute.text) else { - throw XMLDeserializationError.attributeDeserializationFailed( - type: "Double", attribute: attribute) - } - return value - } - - public func validate() {} -} - -extension Float: XMLElementDeserializable, XMLAttributeDeserializable { - /** - Attempts to deserialize XML element content to a Float - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized - - returns: the deserialized Float value - */ - public static func deserialize(_ element: XMLElement) throws -> Float { - guard let value = Float(try element.nonEmptyTextOrThrow()) else { - throw XMLDeserializationError.typeConversionFailed(type: "Float", element: element) - } - return value - } - - /** - Attempts to deserialize XML attribute content to a Float - - - parameter attribute: The XMLAttribute to be deserialized - - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be - deserialized - - returns: the deserialized Float value - */ - public static func deserialize(_ attribute: XMLAttribute) throws -> Float { - guard let value = Float(attribute.text) else { - throw XMLDeserializationError.attributeDeserializationFailed( - type: "Float", attribute: attribute) - } - return value - } - - public func validate() {} -} - -extension Bool: XMLElementDeserializable, XMLAttributeDeserializable { - // swiftlint:disable line_length - /** - Attempts to deserialize XML element content to a Bool. This uses NSString's 'boolValue' - described [here](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/#//apple_ref/occ/instp/NSString/boolValue) - - - parameters: - - element: the XMLElement to be deserialized - - throws: an XMLDeserializationError.typeConversionFailed if the element cannot be deserialized - - returns: the deserialized Bool value - */ - public static func deserialize(_ element: XMLElement) throws -> Bool { - let value = Bool(NSString(string: try element.nonEmptyTextOrThrow()).boolValue) - return value - } - - /** - Attempts to deserialize XML attribute content to a Bool. This uses NSString's 'boolValue' - described [here](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/#//apple_ref/occ/instp/NSString/boolValue) - - - parameter attribute: The XMLAttribute to be deserialized - - throws: an XMLDeserializationError.attributeDeserializationFailed if the attribute cannot be - deserialized - - returns: the deserialized Bool value - */ - public static func deserialize(_ attribute: XMLAttribute) throws -> Bool { - let value = Bool(NSString(string: attribute.text).boolValue) - return value - } - // swiftlint:enable line_length - - public func validate() {} -} diff --git a/Source/XMLIndexerDeserializable.swift b/Source/XMLIndexerDeserializable.swift new file mode 100644 index 00000000..a56c844a --- /dev/null +++ b/Source/XMLIndexerDeserializable.swift @@ -0,0 +1,56 @@ +// +// XMLDeserializationError.swift +// SWXMLHash +// +// Copyright (c) 2016 Maciek Grzybowskio, 2022 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. +// + +import Foundation + +/// Provides XMLIndexer deserialization / type transformation support +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 +public extension XMLIndexerDeserializable { + /** + A default implementation that will throw an error if it is called + + - parameters: + - element: the XMLIndexer to be deserialized + - throws: an XMLDeserializationError.implementationIsMissing if no implementation is found + - returns: this won't ever return because of the error being thrown + */ + static func deserialize(_ element: XMLIndexer) throws -> Self { + throw XMLDeserializationError.implementationIsMissing( + method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)") + } + + /** + A default do nothing implementation of validation. + - throws: nothing + */ + func validate() throws {} +} From 8bc4d1ac2b44e58b9426efa4c1f2a505002a4078 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 13:31:47 -0500 Subject: [PATCH 06/16] feat: hook up LocalizedError on errors --- Source/Bool+XMLDeserialization.swift | 2 +- Source/Double+XMLDeserialization.swift | 2 +- Source/Float+XMLDeserialization.swift | 2 +- Source/Int+XMLDeserialization.swift | 2 +- Source/String+XMLDeserialization.swift | 2 +- Source/XMLAttributeDeserializable.swift | 2 +- Source/XMLDeserialization.swift | 2 +- Source/XMLDeserializationError.swift | 27 +++++++++++++++++-- Source/XMLElementDeserializable.swift | 2 +- .../XMLIndexer+XMLIndexerDeserializable.swift | 10 +------ Source/XMLIndexerDeserializable.swift | 2 +- .../TypeConversionBasicTypesTests.swift | 2 +- 12 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Source/Bool+XMLDeserialization.swift b/Source/Bool+XMLDeserialization.swift index 37c71d3e..6fd40ee3 100644 --- a/Source/Bool+XMLDeserialization.swift +++ b/Source/Bool+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// Bool+XMLDeserializationError.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/Double+XMLDeserialization.swift b/Source/Double+XMLDeserialization.swift index c91c2d55..cf7aef32 100644 --- a/Source/Double+XMLDeserialization.swift +++ b/Source/Double+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// Double+XMLDeserializationError.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/Float+XMLDeserialization.swift b/Source/Float+XMLDeserialization.swift index 21e151d5..489982a1 100644 --- a/Source/Float+XMLDeserialization.swift +++ b/Source/Float+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// Float+XMLDeserializationError.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/Int+XMLDeserialization.swift b/Source/Int+XMLDeserialization.swift index 9c004b9a..44643e70 100644 --- a/Source/Int+XMLDeserialization.swift +++ b/Source/Int+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// Int+XMLDeserializationError.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/String+XMLDeserialization.swift b/Source/String+XMLDeserialization.swift index 52b60aad..59129f68 100644 --- a/Source/String+XMLDeserialization.swift +++ b/Source/String+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// String+XMLDeserializationError.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/XMLAttributeDeserializable.swift b/Source/XMLAttributeDeserializable.swift index 5bfde355..357d3950 100644 --- a/Source/XMLAttributeDeserializable.swift +++ b/Source/XMLAttributeDeserializable.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// XMLAttributeDeserializable.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/XMLDeserialization.swift b/Source/XMLDeserialization.swift index 5e3f850b..ed72fe82 100644 --- a/Source/XMLDeserialization.swift +++ b/Source/XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// XMLDeserialization.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/XMLDeserializationError.swift b/Source/XMLDeserializationError.swift index f40c1a01..2a0d23df 100644 --- a/Source/XMLDeserializationError.swift +++ b/Source/XMLDeserializationError.swift @@ -28,7 +28,7 @@ import Foundation /// The error that is thrown if there is a problem with deserialization -public enum XMLDeserializationError: Error, CustomStringConvertible { +public enum XMLDeserializationError: Error { case implementationIsMissing(method: String) case nodeIsInvalid(node: String) case nodeHasNoValue @@ -57,8 +57,10 @@ public enum XMLDeserializationError: Error, CustomStringConvertible { public static func AttributeDeserializationFailed(_ attr: String, _ value: String) throws -> XMLDeserializationError { fatalError("unavailable") } -// swiftlint:enable identifier_name +} +/// Implementation for CustomStringConvertible +extension XMLDeserializationError: CustomStringConvertible { /// The text description for the error thrown public var description: String { switch self { @@ -77,3 +79,24 @@ public enum XMLDeserializationError: Error, CustomStringConvertible { } } } + +/// Implementation for LocalizedError +extension XMLDeserializationError: LocalizedError { + /// The textual error description for the error + public var errorDescription: String? { + switch self { + case .implementationIsMissing(let method): + return "This deserialization method is not implemented: \(method)" + case .nodeIsInvalid(let node): + return "This node is invalid: \(node)" + case .nodeHasNoValue: + return "This node is empty" + case let .typeConversionFailed(type, node): + return "Can't convert node \(node) to value of type \(type)" + case let .attributeDoesNotExist(element, attribute): + return "Element \(element) does not contain attribute: \(attribute)" + case let .attributeDeserializationFailed(type, attribute): + return "Can't convert attribute \(attribute) to value of type \(type)" + } + } +} diff --git a/Source/XMLElementDeserializable.swift b/Source/XMLElementDeserializable.swift index 8e1deda6..d006823f 100644 --- a/Source/XMLElementDeserializable.swift +++ b/Source/XMLElementDeserializable.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// XMLElementDeserializable.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index 60e44607..8adecd36 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -1,5 +1,5 @@ // -// XMLHash+TypeConversion.swift +// XMLIndexer.XMLIndexerDeserializable.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio @@ -380,11 +380,7 @@ public extension XMLIndexer { throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLIndexer -> [T?]") } } -} -/*: 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) @@ -508,11 +504,7 @@ extension XMLElement { throw XMLDeserializationError.nodeHasNoValue } -} -/*: 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) diff --git a/Source/XMLIndexerDeserializable.swift b/Source/XMLIndexerDeserializable.swift index a56c844a..ea59f434 100644 --- a/Source/XMLIndexerDeserializable.swift +++ b/Source/XMLIndexerDeserializable.swift @@ -1,5 +1,5 @@ // -// XMLDeserializationError.swift +// XMLIndexerDeserializable.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio, 2022 David Mohundro diff --git a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift index dd10097d..88b8181c 100644 --- a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift @@ -188,7 +188,7 @@ class TypeConversionBasicTypesTests: XCTestCase { case missing } XCTAssertThrowsError(try (parser!["root"]["attr"].value(ofAttribute: Keys.missing) as String)) { error in - print(error) + print(error.localizedDescription) guard error is XMLDeserializationError else { XCTFail("Wrong type of error") return From d7aba5a687bcb68b827a8af4d5033a77ab7e1449 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 13:41:21 -0500 Subject: [PATCH 07/16] refactor: split out XMLHashOptions to own file --- SWXMLHash.xcodeproj/project.pbxproj | 10 ++++ Source/XMLHash.swift | 26 --------- Source/XMLHashOptions.swift | 53 +++++++++++++++++++ .../XMLIndexer+XMLIndexerDeserializable.swift | 40 ++++++++++---- 4 files changed, 94 insertions(+), 35 deletions(-) create mode 100644 Source/XMLHashOptions.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index e3a1699d..0e804ee9 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -51,6 +51,10 @@ 63ED3C252821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; 63ED3C262821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; 63ED3C272821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */; }; + 63ED3C292821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; + 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; + 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; + 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; 6C42BED1205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED2205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED3205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; @@ -176,6 +180,7 @@ 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLIndexerDeserializable.swift; sourceTree = ""; }; 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+XMLDeserialization.swift"; sourceTree = ""; }; + 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLHashOptions.swift; sourceTree = ""; }; 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; 6C42BECC2051834B00137D31 /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; 6C42BED4205183AF00137D31 /* shim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; @@ -295,6 +300,7 @@ CD6083F1196CA106000B4F8D /* Source */ = { isa = PBXGroup; children = ( + 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */, 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */, 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */, 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */, @@ -669,6 +675,7 @@ 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED8205183B000137D31 /* shim.swift in Sources */, 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C292821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C182821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD60840C196CA11D000B4F8D /* XMLHash.swift in Sources */, 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */, @@ -709,6 +716,7 @@ 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED5205183B000137D31 /* shim.swift in Sources */, 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD7934C61A7581F500867857 /* XMLHash.swift in Sources */, 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */, @@ -749,6 +757,7 @@ 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED6205183B000137D31 /* shim.swift in Sources */, 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72731C00B0D900C10B28 /* XMLHash.swift in Sources */, 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */, @@ -789,6 +798,7 @@ 63ED3C232821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, 6C42BED7205183B000137D31 /* shim.swift in Sources */, 6317F1A9282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, + 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72741C00B0E300C10B28 /* XMLHash.swift in Sources */, 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */, diff --git a/Source/XMLHash.swift b/Source/XMLHash.swift index 5ea2acf6..b5dd073b 100644 --- a/Source/XMLHash.swift +++ b/Source/XMLHash.swift @@ -36,32 +36,6 @@ import FoundationXML let rootElementName = "SWXMLHash_Root_Element" -/// Parser options -public class XMLHashOptions { - internal init() {} - - /// determines whether to parse the XML with lazy parsing or not - public var shouldProcessLazily = false - - /// determines whether to parse XML namespaces or not (forwards to - /// `XMLParser.shouldProcessNamespaces`) - public var shouldProcessNamespaces = false - - /// Matching element names, element values, attribute names, attribute values - /// will be case insensitive. This will not affect parsing (data does not change) - public var caseInsensitive = false - - /// 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 XMLHash { let options: XMLHashOptions diff --git a/Source/XMLHashOptions.swift b/Source/XMLHashOptions.swift new file mode 100644 index 00000000..8b49dd38 --- /dev/null +++ b/Source/XMLHashOptions.swift @@ -0,0 +1,53 @@ +// +// XMLHashOptions.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// Parser options +public class XMLHashOptions { + internal init() {} + + /// determines whether to parse the XML with lazy parsing or not + public var shouldProcessLazily = false + + /// determines whether to parse XML namespaces or not (forwards to + /// `XMLParser.shouldProcessNamespaces`) + public var shouldProcessNamespaces = false + + /// Matching element names, element values, attribute names, attribute values + /// will be case insensitive. This will not affect parsing (data does not change) + public var caseInsensitive = false + + /// 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 +} + diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/XMLIndexer+XMLIndexerDeserializable.swift index 8adecd36..96ddfb90 100644 --- a/Source/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/XMLIndexer+XMLIndexerDeserializable.swift @@ -79,9 +79,13 @@ public extension XMLIndexer { func value(ofAttribute attr: String) throws -> [T] { switch self { case .list(let elements): - return try elements.map { try $0.value(ofAttribute: attr) } + return try elements.map { + try $0.value(ofAttribute: attr) + } case .element(let element): - return try [element].map { try $0.value(ofAttribute: attr) } + return try [element].map { + try $0.value(ofAttribute: attr) + } case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) case .xmlError(let indexingError): @@ -102,9 +106,13 @@ public extension XMLIndexer { func value(ofAttribute attr: String) throws -> [T]? { switch self { case .list(let elements): - return try elements.map { try $0.value(ofAttribute: attr) } + return try elements.map { + try $0.value(ofAttribute: attr) + } case .element(let element): - return try [element].map { try $0.value(ofAttribute: attr) } + return try [element].map { + try $0.value(ofAttribute: attr) + } case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) default: @@ -123,9 +131,13 @@ public extension XMLIndexer { func value(ofAttribute attr: String) throws -> [T?] { switch self { case .list(let elements): - return elements.map { $0.value(ofAttribute: attr) } + return elements.map { + $0.value(ofAttribute: attr) + } case .element(let element): - return [element].map { $0.value(ofAttribute: attr) } + return [element].map { + $0.value(ofAttribute: attr) + } case .stream(let opStream): return try opStream.findElements().value(ofAttribute: attr) case .xmlError(let indexingError): @@ -306,13 +318,13 @@ public extension XMLIndexer { switch self { case .list(let elements): return try elements.map { - let deserialized = try T.deserialize( XMLIndexer($0) ) + let deserialized = try T.deserialize(XMLIndexer($0)) try deserialized.validate() return deserialized } case .element(let element): return try [element].map { - let deserialized = try T.deserialize( XMLIndexer($0) ) + let deserialized = try T.deserialize(XMLIndexer($0)) try deserialized.validate() return deserialized } @@ -380,7 +392,11 @@ public extension XMLIndexer { throw XMLDeserializationError.nodeIsInvalid(node: "Unexpected error deserializing XMLIndexer -> [T?]") } } +} +/*: 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) @@ -483,7 +499,9 @@ extension XMLElement { public func value(ofAttribute attr: String) -> T? { if let attr = self.attribute(by: attr) { let deserialized = try? T.deserialize(attr) - if deserialized != nil { try? deserialized?.validate() } + if deserialized != nil { + try? deserialized?.validate() + } return deserialized } else { return nil @@ -504,7 +522,11 @@ extension XMLElement { throw XMLDeserializationError.nodeHasNoValue } +} +/*: Provides XMLIndexer 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) From c2fae03920e512ccf0e541e0417de31f12db3d3e Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 13:51:23 -0500 Subject: [PATCH 08/16] refactor: break out IndexingError to own file --- SWXMLHash.xcodeproj/project.pbxproj | 12 ++- Source/IndexingError.swift | 115 ++++++++++++++++++++++++++++ Source/XMLHash.swift | 66 ---------------- 3 files changed, 126 insertions(+), 67 deletions(-) create mode 100644 Source/IndexingError.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index 0e804ee9..f4150d1e 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -55,6 +55,10 @@ 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; + 63ED3C2E2821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; + 63ED3C2F2821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; + 63ED3C302821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; + 63ED3C312821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; 6C42BED1205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED2205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED3205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; @@ -181,6 +185,7 @@ 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLHashOptions.swift; sourceTree = ""; }; + 63ED3C2D2821B100006A8A08 /* IndexingError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexingError.swift; sourceTree = ""; }; 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; 6C42BECC2051834B00137D31 /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; 6C42BED4205183AF00137D31 /* shim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; @@ -300,10 +305,10 @@ CD6083F1196CA106000B4F8D /* Source */ = { isa = PBXGroup; children = ( - 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */, 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */, 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */, 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */, + 63ED3C2D2821B100006A8A08 /* IndexingError.swift */, 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */, 6C42BED4205183AF00137D31 /* shim.swift */, 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */, @@ -314,6 +319,7 @@ 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, + 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */, 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */, ); @@ -670,6 +676,7 @@ buildActionMask = 2147483647; files = ( 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C2E2821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C142821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, @@ -711,6 +718,7 @@ buildActionMask = 2147483647; files = ( 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C2F2821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C152821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, @@ -752,6 +760,7 @@ buildActionMask = 2147483647; files = ( 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C302821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C162821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, @@ -793,6 +802,7 @@ buildActionMask = 2147483647; files = ( 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C312821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C132821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C172821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C232821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, diff --git a/Source/IndexingError.swift b/Source/IndexingError.swift new file mode 100644 index 00000000..e1bde8c8 --- /dev/null +++ b/Source/IndexingError.swift @@ -0,0 +1,115 @@ +// +// IndexingError.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// Error type that is thrown when an indexing or parsing operation fails. +public enum IndexingError: Error { + case attribute(attr: String) + case attributeValue(attr: String, value: String) + case key(key: String) + case index(idx: Int) + case initialize(instance: AnyObject) + case encoding + case error + +// swiftlint:disable identifier_name + // unavailable + @available(*, unavailable, renamed: "attribute(attr:)") + 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") + } +// swiftlint:enable identifier_name +} + +extension IndexingError: CustomStringConvertible { + /// The description for the `IndexingError`. + public var description: String { + switch self { + case .attribute(let attr): + return "XML Attribute Error: Missing attribute [\"\(attr)\"]" + 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)\"]" + case .index(let index): + return "XML Element Error: Incorrect index [\"\(index)\"]" + case .initialize(let instance): + return "XML Indexer Error: initialization with Object [\"\(instance)\"]" + case .encoding: + return "String Encoding Error" + case .error: + return "Unknown Error" + } + } +} + +extension IndexingError: LocalizedError { + /// The description for the `IndexingError`. + public var errorDescription: String? { + switch self { + case .attribute(let attr): + return "XML Attribute Error: Missing attribute [\"\(attr)\"]" + 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)\"]" + case .index(let index): + return "XML Element Error: Incorrect index [\"\(index)\"]" + case .initialize(let instance): + return "XML Indexer Error: initialization with Object [\"\(instance)\"]" + case .encoding: + return "String Encoding Error" + case .error: + return "Unknown Error" + } + } +} + diff --git a/Source/XMLHash.swift b/Source/XMLHash.swift index b5dd073b..40cd910d 100644 --- a/Source/XMLHash.swift +++ b/Source/XMLHash.swift @@ -474,50 +474,6 @@ public struct ParsingError: Error { public let column: Int } -/// Error type that is thrown when an indexing or parsing operation fails. -public enum IndexingError: Error { - case attribute(attr: String) - case attributeValue(attr: String, value: String) - case key(key: String) - case index(idx: Int) - case initialize(instance: AnyObject) - case encoding - case error - -// swiftlint:disable identifier_name - // unavailable - @available(*, unavailable, renamed: "attribute(attr:)") - 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") - } -// swiftlint:enable identifier_name -} - /// Returned from XMLHash, allows easy element lookup into XML data. public enum XMLIndexer { case element(XMLElement) @@ -805,28 +761,6 @@ extension XMLIndexer: CustomStringConvertible { } } -extension IndexingError: CustomStringConvertible { - /// The description for the `IndexingError`. - public var description: String { - switch self { - case .attribute(let attr): - return "XML Attribute Error: Missing attribute [\"\(attr)\"]" - 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)\"]" - case .index(let index): - return "XML Element Error: Incorrect index [\"\(index)\"]" - case .initialize(let instance): - return "XML Indexer Error: initialization with Object [\"\(instance)\"]" - case .encoding: - return "String Encoding Error" - case .error: - return "Unknown Error" - } - } -} - /// Models content for an XML doc, whether it is text or XML public protocol XMLContent: CustomStringConvertible { } From 8c6b0ce7cf0008d262fafb570ff089ec10aee0b0 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 15:37:38 -0500 Subject: [PATCH 09/16] refactor: add in some folders to help with org --- SWXMLHash.xcodeproj/project.pbxproj | 48 ++++++++++++++----- .../XMLAttributeDeserializable.swift | 0 .../XMLDeserialization.swift | 0 .../XMLElementDeserializable.swift | 0 .../XMLIndexer+XMLIndexerDeserializable.swift | 0 .../XMLIndexerDeserializable.swift | 0 .../Bool+XMLDeserialization.swift | 0 .../Double+XMLDeserialization.swift | 0 .../Float+XMLDeserialization.swift | 0 .../Int+XMLDeserialization.swift | 0 .../String+XMLDeserialization.swift | 0 Source/IndexingError.swift | 1 - Source/XMLHashOptions.swift | 1 - 13 files changed, 36 insertions(+), 14 deletions(-) rename Source/{ => Deserialization}/XMLAttributeDeserializable.swift (100%) rename Source/{ => Deserialization}/XMLDeserialization.swift (100%) rename Source/{ => Deserialization}/XMLElementDeserializable.swift (100%) rename Source/{ => Deserialization}/XMLIndexer+XMLIndexerDeserializable.swift (100%) rename Source/{ => Deserialization}/XMLIndexerDeserializable.swift (100%) rename Source/{ => DeserializationTypes}/Bool+XMLDeserialization.swift (100%) rename Source/{ => DeserializationTypes}/Double+XMLDeserialization.swift (100%) rename Source/{ => DeserializationTypes}/Float+XMLDeserialization.swift (100%) rename Source/{ => DeserializationTypes}/Int+XMLDeserialization.swift (100%) rename Source/{ => DeserializationTypes}/String+XMLDeserialization.swift (100%) diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index f4150d1e..eae120ea 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -272,6 +272,39 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 63ED3C4D2821B1DB006A8A08 /* DeserializationTypes */ = { + isa = PBXGroup; + children = ( + 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */, + 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */, + 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */, + 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */, + 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */, + ); + path = DeserializationTypes; + sourceTree = ""; + }; + 63ED3C4E2821B206006A8A08 /* Errors */ = { + isa = PBXGroup; + children = ( + 63ED3C2D2821B100006A8A08 /* IndexingError.swift */, + 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, + ); + name = Errors; + sourceTree = ""; + }; + 63ED3C4F2821C9D4006A8A08 /* Deserialization */ = { + isa = PBXGroup; + children = ( + 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */, + 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */, + 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */, + 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, + 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */, + ); + path = Deserialization; + sourceTree = ""; + }; CD291F1E1BF635BE009A1FA6 /* tvOS */ = { isa = PBXGroup; children = ( @@ -305,23 +338,14 @@ CD6083F1196CA106000B4F8D /* Source */ = { isa = PBXGroup; children = ( - 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */, - 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */, - 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */, - 63ED3C2D2821B100006A8A08 /* IndexingError.swift */, - 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */, + 63ED3C4F2821C9D4006A8A08 /* Deserialization */, + 63ED3C4D2821B1DB006A8A08 /* DeserializationTypes */, + 63ED3C4E2821B206006A8A08 /* Errors */, 6C42BED4205183AF00137D31 /* shim.swift */, - 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */, CD6083F2196CA106000B4F8D /* Supporting Files */, CD6083F4196CA106000B4F8D /* SWXMLHash.h */, - 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */, - 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */, - 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, - 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */, - 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, - 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */, ); path = Source; sourceTree = ""; diff --git a/Source/XMLAttributeDeserializable.swift b/Source/Deserialization/XMLAttributeDeserializable.swift similarity index 100% rename from Source/XMLAttributeDeserializable.swift rename to Source/Deserialization/XMLAttributeDeserializable.swift diff --git a/Source/XMLDeserialization.swift b/Source/Deserialization/XMLDeserialization.swift similarity index 100% rename from Source/XMLDeserialization.swift rename to Source/Deserialization/XMLDeserialization.swift diff --git a/Source/XMLElementDeserializable.swift b/Source/Deserialization/XMLElementDeserializable.swift similarity index 100% rename from Source/XMLElementDeserializable.swift rename to Source/Deserialization/XMLElementDeserializable.swift diff --git a/Source/XMLIndexer+XMLIndexerDeserializable.swift b/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift similarity index 100% rename from Source/XMLIndexer+XMLIndexerDeserializable.swift rename to Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift diff --git a/Source/XMLIndexerDeserializable.swift b/Source/Deserialization/XMLIndexerDeserializable.swift similarity index 100% rename from Source/XMLIndexerDeserializable.swift rename to Source/Deserialization/XMLIndexerDeserializable.swift diff --git a/Source/Bool+XMLDeserialization.swift b/Source/DeserializationTypes/Bool+XMLDeserialization.swift similarity index 100% rename from Source/Bool+XMLDeserialization.swift rename to Source/DeserializationTypes/Bool+XMLDeserialization.swift diff --git a/Source/Double+XMLDeserialization.swift b/Source/DeserializationTypes/Double+XMLDeserialization.swift similarity index 100% rename from Source/Double+XMLDeserialization.swift rename to Source/DeserializationTypes/Double+XMLDeserialization.swift diff --git a/Source/Float+XMLDeserialization.swift b/Source/DeserializationTypes/Float+XMLDeserialization.swift similarity index 100% rename from Source/Float+XMLDeserialization.swift rename to Source/DeserializationTypes/Float+XMLDeserialization.swift diff --git a/Source/Int+XMLDeserialization.swift b/Source/DeserializationTypes/Int+XMLDeserialization.swift similarity index 100% rename from Source/Int+XMLDeserialization.swift rename to Source/DeserializationTypes/Int+XMLDeserialization.swift diff --git a/Source/String+XMLDeserialization.swift b/Source/DeserializationTypes/String+XMLDeserialization.swift similarity index 100% rename from Source/String+XMLDeserialization.swift rename to Source/DeserializationTypes/String+XMLDeserialization.swift diff --git a/Source/IndexingError.swift b/Source/IndexingError.swift index e1bde8c8..044a5070 100644 --- a/Source/IndexingError.swift +++ b/Source/IndexingError.swift @@ -112,4 +112,3 @@ extension IndexingError: LocalizedError { } } } - diff --git a/Source/XMLHashOptions.swift b/Source/XMLHashOptions.swift index 8b49dd38..56794747 100644 --- a/Source/XMLHashOptions.swift +++ b/Source/XMLHashOptions.swift @@ -50,4 +50,3 @@ public class XMLHashOptions { /// attempt to handle HTML which isn't always XML-compatible public var detectParsingErrors = false } - From 9ee07293d08307e21ec21eb25f14c46a66bc5456 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 16:27:01 -0500 Subject: [PATCH 10/16] refactor: more breaking out of files --- SWXMLHash.xcodeproj/project.pbxproj | 90 ++++-- Source/{ => Errors}/IndexingError.swift | 0 Source/Errors/ParsingError.swift | 43 +++ .../XMLDeserializationError.swift | 0 Source/FullXMLParser.swift | 107 ++++++++ Source/IndexOp.swift | 79 ++++++ Source/LazyXMLParser.swift | 123 +++++++++ Source/Stack.swift | 50 ++++ Source/XMLHash.swift | 258 ------------------ 9 files changed, 472 insertions(+), 278 deletions(-) rename Source/{ => Errors}/IndexingError.swift (100%) create mode 100644 Source/Errors/ParsingError.swift rename Source/{ => Errors}/XMLDeserializationError.swift (100%) create mode 100644 Source/FullXMLParser.swift create mode 100644 Source/IndexOp.swift create mode 100644 Source/LazyXMLParser.swift create mode 100644 Source/Stack.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index eae120ea..ab72effa 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -11,10 +11,6 @@ 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; - 6317F1A12821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; - 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; - 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; - 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */; }; 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; @@ -55,10 +51,34 @@ 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */; }; - 63ED3C2E2821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; - 63ED3C2F2821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; - 63ED3C302821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; - 63ED3C312821B100006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C2D2821B100006A8A08 /* IndexingError.swift */; }; + 63ED3C522821D230006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C502821D230006A8A08 /* IndexingError.swift */; }; + 63ED3C532821D230006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C502821D230006A8A08 /* IndexingError.swift */; }; + 63ED3C542821D230006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C502821D230006A8A08 /* IndexingError.swift */; }; + 63ED3C552821D230006A8A08 /* IndexingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C502821D230006A8A08 /* IndexingError.swift */; }; + 63ED3C562821D230006A8A08 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */; }; + 63ED3C572821D230006A8A08 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */; }; + 63ED3C582821D230006A8A08 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */; }; + 63ED3C592821D230006A8A08 /* XMLDeserializationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */; }; + 63ED3C5E2821D247006A8A08 /* IndexOp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5A2821D247006A8A08 /* IndexOp.swift */; }; + 63ED3C5F2821D247006A8A08 /* IndexOp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5A2821D247006A8A08 /* IndexOp.swift */; }; + 63ED3C602821D247006A8A08 /* IndexOp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5A2821D247006A8A08 /* IndexOp.swift */; }; + 63ED3C612821D247006A8A08 /* IndexOp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5A2821D247006A8A08 /* IndexOp.swift */; }; + 63ED3C622821D247006A8A08 /* LazyXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */; }; + 63ED3C632821D247006A8A08 /* LazyXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */; }; + 63ED3C642821D247006A8A08 /* LazyXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */; }; + 63ED3C652821D247006A8A08 /* LazyXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */; }; + 63ED3C662821D247006A8A08 /* FullXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */; }; + 63ED3C672821D247006A8A08 /* FullXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */; }; + 63ED3C682821D247006A8A08 /* FullXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */; }; + 63ED3C692821D247006A8A08 /* FullXMLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */; }; + 63ED3C6A2821D247006A8A08 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5D2821D247006A8A08 /* Stack.swift */; }; + 63ED3C6B2821D247006A8A08 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5D2821D247006A8A08 /* Stack.swift */; }; + 63ED3C6C2821D247006A8A08 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5D2821D247006A8A08 /* Stack.swift */; }; + 63ED3C6D2821D247006A8A08 /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C5D2821D247006A8A08 /* Stack.swift */; }; + 63ED3C6F2821D311006A8A08 /* ParsingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C6E2821D311006A8A08 /* ParsingError.swift */; }; + 63ED3C702821D311006A8A08 /* ParsingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C6E2821D311006A8A08 /* ParsingError.swift */; }; + 63ED3C712821D311006A8A08 /* ParsingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C6E2821D311006A8A08 /* ParsingError.swift */; }; + 63ED3C722821D311006A8A08 /* ParsingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C6E2821D311006A8A08 /* ParsingError.swift */; }; 6C42BED1205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED2205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; 6C42BED3205183A100137D31 /* shim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C42BECC2051834B00137D31 /* shim.swift */; }; @@ -174,7 +194,6 @@ /* Begin PBXFileReference section */ 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = ""; }; - 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLDeserializationError.swift; sourceTree = ""; }; 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLDeserialization.swift; sourceTree = ""; }; @@ -185,7 +204,13 @@ 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLHashOptions.swift; sourceTree = ""; }; - 63ED3C2D2821B100006A8A08 /* IndexingError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexingError.swift; sourceTree = ""; }; + 63ED3C502821D230006A8A08 /* IndexingError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IndexingError.swift; path = Errors/IndexingError.swift; sourceTree = ""; }; + 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XMLDeserializationError.swift; path = Errors/XMLDeserializationError.swift; sourceTree = ""; }; + 63ED3C5A2821D247006A8A08 /* IndexOp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexOp.swift; sourceTree = ""; }; + 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LazyXMLParser.swift; sourceTree = ""; }; + 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullXMLParser.swift; sourceTree = ""; }; + 63ED3C5D2821D247006A8A08 /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; + 63ED3C6E2821D311006A8A08 /* ParsingError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsingError.swift; path = Errors/ParsingError.swift; sourceTree = ""; }; 6C0CE0F01D7440F8005F1248 /* LinuxShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxShims.swift; sourceTree = ""; }; 6C42BECC2051834B00137D31 /* shim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; 6C42BED4205183AF00137D31 /* shim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = shim.swift; sourceTree = ""; }; @@ -287,8 +312,9 @@ 63ED3C4E2821B206006A8A08 /* Errors */ = { isa = PBXGroup; children = ( - 63ED3C2D2821B100006A8A08 /* IndexingError.swift */, - 6317F1A02821756400F6C364 /* XMLDeserializationError.swift */, + 63ED3C6E2821D311006A8A08 /* ParsingError.swift */, + 63ED3C502821D230006A8A08 /* IndexingError.swift */, + 63ED3C512821D230006A8A08 /* XMLDeserializationError.swift */, ); name = Errors; sourceTree = ""; @@ -341,7 +367,11 @@ 63ED3C4F2821C9D4006A8A08 /* Deserialization */, 63ED3C4D2821B1DB006A8A08 /* DeserializationTypes */, 63ED3C4E2821B206006A8A08 /* Errors */, + 63ED3C5C2821D247006A8A08 /* FullXMLParser.swift */, + 63ED3C5A2821D247006A8A08 /* IndexOp.swift */, + 63ED3C5B2821D247006A8A08 /* LazyXMLParser.swift */, 6C42BED4205183AF00137D31 /* shim.swift */, + 63ED3C5D2821D247006A8A08 /* Stack.swift */, CD6083F2196CA106000B4F8D /* Supporting Files */, CD6083F4196CA106000B4F8D /* SWXMLHash.h */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, @@ -700,20 +730,25 @@ buildActionMask = 2147483647; files = ( 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, - 63ED3C2E2821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C142821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C662821D247006A8A08 /* FullXMLParser.swift in Sources */, 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, + 63ED3C622821D247006A8A08 /* LazyXMLParser.swift in Sources */, 6C42BED8205183B000137D31 /* shim.swift in Sources */, 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, 63ED3C292821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C182821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD60840C196CA11D000B4F8D /* XMLHash.swift in Sources */, 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */, - 6317F1A12821756400F6C364 /* XMLDeserializationError.swift in Sources */, 63ED3C082821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C562821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C242821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, + 63ED3C522821D230006A8A08 /* IndexingError.swift in Sources */, 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 63ED3C6A2821D247006A8A08 /* Stack.swift in Sources */, + 63ED3C5E2821D247006A8A08 /* IndexOp.swift in Sources */, + 63ED3C6F2821D311006A8A08 /* ParsingError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -742,20 +777,25 @@ buildActionMask = 2147483647; files = ( 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, - 63ED3C2F2821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C152821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C672821D247006A8A08 /* FullXMLParser.swift in Sources */, 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, + 63ED3C632821D247006A8A08 /* LazyXMLParser.swift in Sources */, 6C42BED5205183B000137D31 /* shim.swift in Sources */, 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD7934C61A7581F500867857 /* XMLHash.swift in Sources */, 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */, - 6317F1A22821756400F6C364 /* XMLDeserializationError.swift in Sources */, 63ED3C092821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C572821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C252821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, + 63ED3C532821D230006A8A08 /* IndexingError.swift in Sources */, 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 63ED3C6B2821D247006A8A08 /* Stack.swift in Sources */, + 63ED3C5F2821D247006A8A08 /* IndexOp.swift in Sources */, + 63ED3C702821D311006A8A08 /* ParsingError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -784,20 +824,25 @@ buildActionMask = 2147483647; files = ( 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, - 63ED3C302821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C162821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C682821D247006A8A08 /* FullXMLParser.swift in Sources */, 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, + 63ED3C642821D247006A8A08 /* LazyXMLParser.swift in Sources */, 6C42BED6205183B000137D31 /* shim.swift in Sources */, 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72731C00B0D900C10B28 /* XMLHash.swift in Sources */, 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */, - 6317F1A32821756400F6C364 /* XMLDeserializationError.swift in Sources */, 63ED3C0A2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C582821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C262821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, + 63ED3C542821D230006A8A08 /* IndexingError.swift in Sources */, 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 63ED3C6C2821D247006A8A08 /* Stack.swift in Sources */, + 63ED3C602821D247006A8A08 /* IndexOp.swift in Sources */, + 63ED3C712821D311006A8A08 /* ParsingError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -826,20 +871,25 @@ buildActionMask = 2147483647; files = ( 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, - 63ED3C312821B100006A8A08 /* IndexingError.swift in Sources */, 63ED3C132821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C172821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, + 63ED3C692821D247006A8A08 /* FullXMLParser.swift in Sources */, 63ED3C232821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */, + 63ED3C652821D247006A8A08 /* LazyXMLParser.swift in Sources */, 6C42BED7205183B000137D31 /* shim.swift in Sources */, 6317F1A9282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */, 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72741C00B0E300C10B28 /* XMLHash.swift in Sources */, 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */, - 6317F1A42821756400F6C364 /* XMLDeserializationError.swift in Sources */, 63ED3C0B2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, + 63ED3C592821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C272821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, + 63ED3C552821D230006A8A08 /* IndexingError.swift in Sources */, 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 63ED3C6D2821D247006A8A08 /* Stack.swift in Sources */, + 63ED3C612821D247006A8A08 /* IndexOp.swift in Sources */, + 63ED3C722821D311006A8A08 /* ParsingError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/IndexingError.swift b/Source/Errors/IndexingError.swift similarity index 100% rename from Source/IndexingError.swift rename to Source/Errors/IndexingError.swift diff --git a/Source/Errors/ParsingError.swift b/Source/Errors/ParsingError.swift new file mode 100644 index 00000000..8d1c73dc --- /dev/null +++ b/Source/Errors/ParsingError.swift @@ -0,0 +1,43 @@ +// +// ParsingError.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +public struct ParsingError: Error { + public let line: Int + public let column: Int +} + +extension ParsingError: CustomStringConvertible { + public var description: String { + "There was a parsing error on line: \(line), column: \(column)" + } +} + +extension ParsingError: LocalizedError { + public var errorDescription: String? { + "There was a parsing error on line: \(line), column: \(column)" + } +} diff --git a/Source/XMLDeserializationError.swift b/Source/Errors/XMLDeserializationError.swift similarity index 100% rename from Source/XMLDeserializationError.swift rename to Source/Errors/XMLDeserializationError.swift diff --git a/Source/FullXMLParser.swift b/Source/FullXMLParser.swift new file mode 100644 index 00000000..55a3c03d --- /dev/null +++ b/Source/FullXMLParser.swift @@ -0,0 +1,107 @@ +// +// FullXMLParser.swift +// SWXMLHash +// +// Copyright (c) 2014 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. +// + +import Foundation + +/// The implementation of XMLParserDelegate and where the parsing actually happens. +class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { + required init(_ options: XMLHashOptions) { + root = XMLElement(name: rootElementName, options: options) + self.options = options + super.init() + } + + let root: XMLElement + var parentStack = Stack() + let options: XMLHashOptions + var parsingError: ParsingError? + + func parse(_ data: Data) -> XMLIndexer { + // clear any prior runs of parse... expected that this won't be necessary, + // but you never know + parentStack.removeAll() + + parentStack.push(root) + + let parser = XMLParser(data: data) + parser.shouldProcessNamespaces = options.shouldProcessNamespaces + parser.delegate = self + _ = parser.parse() + + if options.detectParsingErrors, let err = parsingError { + return XMLIndexer.parsingError(err) + } else { + return XMLIndexer(root) + } + } + + 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) + + parentStack.push(currentNode) + } + + func parser(_ parser: XMLParser, foundCharacters string: String) { + let current = parentStack.top() + + current.addText(string) + } + + 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 + } +} diff --git a/Source/IndexOp.swift b/Source/IndexOp.swift new file mode 100644 index 00000000..6c36947e --- /dev/null +++ b/Source/IndexOp.swift @@ -0,0 +1,79 @@ +// +// IndexOp.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// Represents an indexed operation against a lazily parsed `XMLIndexer` +public class IndexOp { + var index: Int + let key: String + + init(_ key: String) { + self.key = key + self.index = -1 + } + + func toString() -> String { + if index >= 0 { + return key + " " + index.description + } + + return key + } +} + +/// Represents a collection of `IndexOp` instances. Provides a means of iterating them +/// to find a match in a lazily parsed `XMLIndexer` instance. +public class IndexOps { + var ops: [IndexOp] = [] + + let parser: LazyXMLParser + + init(parser: LazyXMLParser) { + self.parser = parser + } + + func findElements() -> XMLIndexer { + parser.startParsing(ops) + let indexer = XMLIndexer(parser.root) + var childIndex = indexer + for oper in ops { + childIndex = childIndex[oper.key] + if oper.index >= 0 { + childIndex = childIndex[oper.index] + } + } + ops.removeAll(keepingCapacity: false) + return childIndex + } + + func stringify() -> String { + var ret = "" + for oper in ops { + ret += "[" + oper.toString() + "]" + } + return ret + } +} diff --git a/Source/LazyXMLParser.swift b/Source/LazyXMLParser.swift new file mode 100644 index 00000000..148cf4f9 --- /dev/null +++ b/Source/LazyXMLParser.swift @@ -0,0 +1,123 @@ +// +// LazyXMLParser.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// The implementation of XMLParserDelegate and where the lazy parsing actually happens. +class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { + required init(_ options: XMLHashOptions) { + root = XMLElement(name: rootElementName, options: options) + self.options = options + super.init() + } + + var root: XMLElement + var parentStack = Stack() + var elementStack = Stack() + + var data: Data? + var ops: [IndexOp] = [] + let options: XMLHashOptions + + func parse(_ data: Data) -> XMLIndexer { + self.data = data + return XMLIndexer(self) + } + + func startParsing(_ ops: [IndexOp]) { + // reset state for a new lazy parsing run + root = XMLElement(name: rootElementName, options: root.options) + parentStack.removeAll() + parentStack.push(root) + + self.ops = ops + let parser = XMLParser(data: data!) + parser.shouldProcessNamespaces = options.shouldProcessNamespaces + parser.delegate = self + _ = parser.parse() + } + + func parser(_ parser: XMLParser, + didStartElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?, + attributes attributeDict: [String: String]) { + elementStack.push(elementName) + + if !onMatch() { + return + } + + let currentNode = parentStack + .top() + .addElement(elementName, withAttributes: attributeDict, caseInsensitive: self.options.caseInsensitive) + parentStack.push(currentNode) + } + + func parser(_ parser: XMLParser, foundCharacters string: String) { + if !onMatch() { + return + } + + let current = parentStack.top() + + current.addText(string) + } + + 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() + + if match { + parentStack.drop() + } + } + + func onMatch() -> Bool { + // we typically want to compare against the elementStack to see if it matches ops, *but* + // if we're on the first element, we'll instead compare the other direction. + if elementStack.items.count > ops.count { + return elementStack.items.starts(with: ops.map { $0.key }) + } else { + return ops.map { $0.key }.starts(with: elementStack.items) + } + } +} diff --git a/Source/Stack.swift b/Source/Stack.swift new file mode 100644 index 00000000..e966185d --- /dev/null +++ b/Source/Stack.swift @@ -0,0 +1,50 @@ +// +// Stack.swift +// SWXMLHash +// +// Copyright (c) 2014 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. +// + +import Foundation + +struct Stack { + var items = [T]() + + mutating func push(_ item: T) { + items.append(item) + } + + mutating func pop() -> T { + items.removeLast() + } + + mutating func drop() { + _ = pop() + } + + mutating func removeAll() { + items.removeAll(keepingCapacity: false) + } + + func top() -> T { + items[items.count - 1] + } +} diff --git a/Source/XMLHash.swift b/Source/XMLHash.swift index 40cd910d..91dca0de 100644 --- a/Source/XMLHash.swift +++ b/Source/XMLHash.swift @@ -128,30 +128,6 @@ public class XMLHash { } } -struct Stack { - var items = [T]() - - mutating func push(_ item: T) { - items.append(item) - } - - mutating func pop() -> T { - items.removeLast() - } - - mutating func drop() { - _ = pop() - } - - mutating func removeAll() { - items.removeAll(keepingCapacity: false) - } - - func top() -> T { - items[items.count - 1] - } -} - protocol SimpleXmlParser { init(_ options: XMLHashOptions) func parse(_ data: Data) -> XMLIndexer @@ -240,240 +216,6 @@ extension XMLParserDelegate { #endif -/// The implementation of XMLParserDelegate and where the lazy parsing actually happens. -class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { - required init(_ options: XMLHashOptions) { - root = XMLElement(name: rootElementName, options: options) - self.options = options - super.init() - } - - var root: XMLElement - var parentStack = Stack() - var elementStack = Stack() - - var data: Data? - var ops: [IndexOp] = [] - let options: XMLHashOptions - - func parse(_ data: Data) -> XMLIndexer { - self.data = data - return XMLIndexer(self) - } - - func startParsing(_ ops: [IndexOp]) { - // reset state for a new lazy parsing run - root = XMLElement(name: rootElementName, options: root.options) - parentStack.removeAll() - parentStack.push(root) - - self.ops = ops - let parser = XMLParser(data: data!) - parser.shouldProcessNamespaces = options.shouldProcessNamespaces - parser.delegate = self - _ = parser.parse() - } - - func parser(_ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String]) { - elementStack.push(elementName) - - if !onMatch() { - return - } - - let currentNode = parentStack - .top() - .addElement(elementName, withAttributes: attributeDict, caseInsensitive: self.options.caseInsensitive) - parentStack.push(currentNode) - } - - func parser(_ parser: XMLParser, foundCharacters string: String) { - if !onMatch() { - return - } - - let current = parentStack.top() - - current.addText(string) - } - - 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() - - if match { - parentStack.drop() - } - } - - func onMatch() -> Bool { - // we typically want to compare against the elementStack to see if it matches ops, *but* - // if we're on the first element, we'll instead compare the other direction. - if elementStack.items.count > ops.count { - return elementStack.items.starts(with: ops.map { $0.key }) - } else { - return ops.map { $0.key }.starts(with: elementStack.items) - } - } -} - -/// The implementation of XMLParserDelegate and where the parsing actually happens. -class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { - required init(_ options: XMLHashOptions) { - root = XMLElement(name: rootElementName, options: options) - self.options = options - super.init() - } - - let root: XMLElement - var parentStack = Stack() - let options: XMLHashOptions - var parsingError: ParsingError? - - func parse(_ data: Data) -> XMLIndexer { - // clear any prior runs of parse... expected that this won't be necessary, - // but you never know - parentStack.removeAll() - - parentStack.push(root) - - let parser = XMLParser(data: data) - parser.shouldProcessNamespaces = options.shouldProcessNamespaces - parser.delegate = self - _ = parser.parse() - - if options.detectParsingErrors, let err = parsingError { - return XMLIndexer.parsingError(err) - } else { - return XMLIndexer(root) - } - } - - 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) - - parentStack.push(currentNode) - } - - func parser(_ parser: XMLParser, foundCharacters string: String) { - let current = parentStack.top() - - current.addText(string) - } - - 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` -public class IndexOp { - var index: Int - let key: String - - init(_ key: String) { - self.key = key - self.index = -1 - } - - func toString() -> String { - if index >= 0 { - return key + " " + index.description - } - - return key - } -} - -/// Represents a collection of `IndexOp` instances. Provides a means of iterating them -/// to find a match in a lazily parsed `XMLIndexer` instance. -public class IndexOps { - var ops: [IndexOp] = [] - - let parser: LazyXMLParser - - init(parser: LazyXMLParser) { - self.parser = parser - } - - func findElements() -> XMLIndexer { - parser.startParsing(ops) - let indexer = XMLIndexer(parser.root) - var childIndex = indexer - for oper in ops { - childIndex = childIndex[oper.key] - if oper.index >= 0 { - childIndex = childIndex[oper.index] - } - } - ops.removeAll(keepingCapacity: false) - return childIndex - } - - func stringify() -> String { - var ret = "" - for oper in ops { - ret += "[" + oper.toString() + "]" - } - return ret - } -} - -public struct ParsingError: Error { - public let line: Int - public let column: Int -} - /// Returned from XMLHash, allows easy element lookup into XML data. public enum XMLIndexer { case element(XMLElement) From 41df6411298b3f0504767d79700ff91b50bb2b6d Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 18:46:40 -0500 Subject: [PATCH 11/16] refactor: break out into even more files --- SWXMLHash.xcodeproj/project.pbxproj | 60 +++ Source/String+Extensions.swift | 39 ++ Source/TextElement.swift | 43 +++ Source/XMLAttribute.swift | 43 +++ Source/XMLContent.swift | 29 ++ Source/XMLElement.swift | 179 +++++++++ Source/XMLHash.swift | 551 ---------------------------- Source/XMLIndexer.swift | 364 ++++++++++++++++++ 8 files changed, 757 insertions(+), 551 deletions(-) create mode 100644 Source/String+Extensions.swift create mode 100644 Source/TextElement.swift create mode 100644 Source/XMLAttribute.swift create mode 100644 Source/XMLContent.swift create mode 100644 Source/XMLElement.swift create mode 100644 Source/XMLIndexer.swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index ab72effa..9cbf1ce9 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -11,6 +11,30 @@ 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; + 5AF0E03C7AC0B144A711143C /* XMLElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */; }; + 5AF0E228F0D4FD1B85A66B5E /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; + 5AF0E2C961494B70E578CFF2 /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; + 5AF0E2DFC1636E975797A394 /* XMLAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */; }; + 5AF0E2F3CA5A347BBE64EDC5 /* TextElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E710E1B7BA748FBC986B /* TextElement.swift */; }; + 5AF0E3BD03782E18AA13F24A /* TextElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E710E1B7BA748FBC986B /* TextElement.swift */; }; + 5AF0E4FBCBF97B710D484E96 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */; }; + 5AF0E509C3E12F83B1E8D2B3 /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; + 5AF0E51E7FB4BC99D267F72F /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */; }; + 5AF0E5270F4914C039407BD0 /* XMLElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */; }; + 5AF0E586635337278611A179 /* XMLIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */; }; + 5AF0E6E95E6DE770F0383CF4 /* XMLIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */; }; + 5AF0E72422B26DCA16716C20 /* XMLAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */; }; + 5AF0E83E20CB0E145957F255 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */; }; + 5AF0E848394370F0E94BF9A0 /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; + 5AF0E9F306850236785A4512 /* XMLIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */; }; + 5AF0EA10D807D39E5A877DCA /* TextElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E710E1B7BA748FBC986B /* TextElement.swift */; }; + 5AF0EA56B730695C216E4571 /* XMLIndexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */; }; + 5AF0EAD8FF3226490385B398 /* XMLAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */; }; + 5AF0EBE38E3E25CE4A5276E7 /* XMLElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */; }; + 5AF0ED095AF755874AD7EBFF /* XMLAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */; }; + 5AF0ED9738E0194A03FE6D4C /* TextElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E710E1B7BA748FBC986B /* TextElement.swift */; }; + 5AF0EE0166578A68DEB25C84 /* XMLElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */; }; + 5AF0EEA3155F6F08B3F3DB71 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */; }; 6317F1A6282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; 6317F1A7282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; 6317F1A8282179E200F6C364 /* Float+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */; }; @@ -194,6 +218,12 @@ /* Begin PBXFileReference section */ 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = ""; }; + 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLElement.swift; sourceTree = ""; }; + 5AF0E710E1B7BA748FBC986B /* TextElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextElement.swift; sourceTree = ""; }; + 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLContent.swift; sourceTree = ""; }; + 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; + 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLIndexer.swift; sourceTree = ""; }; + 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLAttribute.swift; sourceTree = ""; }; 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLDeserialization.swift; sourceTree = ""; }; @@ -376,6 +406,12 @@ CD6083F4196CA106000B4F8D /* SWXMLHash.h */, CD60840B196CA11D000B4F8D /* XMLHash.swift */, 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */, + 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */, + 5AF0E710E1B7BA748FBC986B /* TextElement.swift */, + 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */, + 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */, + 5AF0ECA326E0FDA144CE093F /* String+Extensions.swift */, + 5AF0ECC2B709D6BF9A68F3D1 /* XMLIndexer.swift */, ); path = Source; sourceTree = ""; @@ -749,6 +785,12 @@ 63ED3C6A2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C5E2821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C6F2821D311006A8A08 /* ParsingError.swift in Sources */, + 5AF0E848394370F0E94BF9A0 /* XMLContent.swift in Sources */, + 5AF0E2F3CA5A347BBE64EDC5 /* TextElement.swift in Sources */, + 5AF0E2DFC1636E975797A394 /* XMLAttribute.swift in Sources */, + 5AF0EBE38E3E25CE4A5276E7 /* XMLElement.swift in Sources */, + 5AF0E83E20CB0E145957F255 /* String+Extensions.swift in Sources */, + 5AF0E9F306850236785A4512 /* XMLIndexer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -796,6 +838,12 @@ 63ED3C6B2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C5F2821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C702821D311006A8A08 /* ParsingError.swift in Sources */, + 5AF0E228F0D4FD1B85A66B5E /* XMLContent.swift in Sources */, + 5AF0E3BD03782E18AA13F24A /* TextElement.swift in Sources */, + 5AF0E72422B26DCA16716C20 /* XMLAttribute.swift in Sources */, + 5AF0EE0166578A68DEB25C84 /* XMLElement.swift in Sources */, + 5AF0EEA3155F6F08B3F3DB71 /* String+Extensions.swift in Sources */, + 5AF0EA56B730695C216E4571 /* XMLIndexer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -843,6 +891,12 @@ 63ED3C6C2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C602821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C712821D311006A8A08 /* ParsingError.swift in Sources */, + 5AF0E2C961494B70E578CFF2 /* XMLContent.swift in Sources */, + 5AF0ED9738E0194A03FE6D4C /* TextElement.swift in Sources */, + 5AF0EAD8FF3226490385B398 /* XMLAttribute.swift in Sources */, + 5AF0E03C7AC0B144A711143C /* XMLElement.swift in Sources */, + 5AF0E51E7FB4BC99D267F72F /* String+Extensions.swift in Sources */, + 5AF0E6E95E6DE770F0383CF4 /* XMLIndexer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -890,6 +944,12 @@ 63ED3C6D2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C612821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C722821D311006A8A08 /* ParsingError.swift in Sources */, + 5AF0E509C3E12F83B1E8D2B3 /* XMLContent.swift in Sources */, + 5AF0EA10D807D39E5A877DCA /* TextElement.swift in Sources */, + 5AF0ED095AF755874AD7EBFF /* XMLAttribute.swift in Sources */, + 5AF0E5270F4914C039407BD0 /* XMLElement.swift in Sources */, + 5AF0E4FBCBF97B710D484E96 /* String+Extensions.swift in Sources */, + 5AF0E586635337278611A179 /* XMLIndexer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/String+Extensions.swift b/Source/String+Extensions.swift new file mode 100644 index 00000000..48b845f2 --- /dev/null +++ b/Source/String+Extensions.swift @@ -0,0 +1,39 @@ +// +// String+Extensions.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +internal extension String { + func compare(_ str2: String?, _ insensitive: Bool) -> Bool { + guard let str2 = str2 else { + return false + } + let str1 = self + if insensitive { + return str1.caseInsensitiveCompare(str2) == .orderedSame + } + return str1 == str2 + } +} diff --git a/Source/TextElement.swift b/Source/TextElement.swift new file mode 100644 index 00000000..fcf46550 --- /dev/null +++ b/Source/TextElement.swift @@ -0,0 +1,43 @@ +// +// TextElement.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// Models a text element +public class TextElement: XMLContent { + /// The underlying text value + public let text: String + + init(text: String) { + self.text = text + } +} + +extension TextElement: CustomStringConvertible { + /// The text value for a `TextElement` instance. + public var description: String { + text + } +} diff --git a/Source/XMLAttribute.swift b/Source/XMLAttribute.swift new file mode 100644 index 00000000..300be3d6 --- /dev/null +++ b/Source/XMLAttribute.swift @@ -0,0 +1,43 @@ +// +// XMLAttribute.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +public struct XMLAttribute { + public let name: String + public let text: String + + init(name: String, text: String) { + self.name = name + self.text = text + } +} + +extension XMLAttribute: CustomStringConvertible { + /// The textual representation of an `XMLAttribute` instance. + public var description: String { + "\(name)=\"\(text)\"" + } +} diff --git a/Source/XMLContent.swift b/Source/XMLContent.swift new file mode 100644 index 00000000..b7be82e8 --- /dev/null +++ b/Source/XMLContent.swift @@ -0,0 +1,29 @@ +// +// XMLContent.swift +// SWXMLHash +// +// Copyright (c) 2014 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. +// + +import Foundation + +/// Models content for an XML doc, whether it is text or XML +public protocol XMLContent: CustomStringConvertible { } diff --git a/Source/XMLElement.swift b/Source/XMLElement.swift new file mode 100644 index 00000000..ca468f6f --- /dev/null +++ b/Source/XMLElement.swift @@ -0,0 +1,179 @@ +// +// XMLElement.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// Models an XML element, including name, text and attributes +public class XMLElement: XMLContent { + /// The name of the element + public let name: String + + /// 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 + } + return allAttributes[name] + } + + /// The inner text of the element, if it exists + public var text: String { + children.reduce("", { + if let element = $1 as? TextElement { + return $0 + element.text + } + + return $0 + }) + } + + /// The inner text of the element and its children + public var recursiveText: String { + children.reduce("", { + if let textElement = $1 as? TextElement { + return $0 + textElement.text + } else if let xmlElement = $1 as? XMLElement { + return $0 + xmlElement.recursiveText + } else { + return $0 + } + }) + } + + 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] { + children.compactMap { $0 as? XMLElement } + } + + /** + Initialize an XMLElement instance + + - 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, options: XMLHashOptions) { + self.name = name + self.index = index + self.options = options + } + + /** + Adds a new XMLElement underneath this instance of XMLElement + + - parameters: + - name: The name of the new element to be added + - withAttributes: The attributes dictionary for the element being added + - returns: The XMLElement that has now been added + */ + + func addElement(_ name: String, withAttributes attributes: [String: String], caseInsensitive: Bool) -> XMLElement { + let element = XMLElement(name: name, index: count, options: options) + count += 1 + + children.append(element) + + for (key, value) in attributes { + element.allAttributes[key] = XMLAttribute(name: key, text: value) + } + + return element + } + + func addText(_ text: String) { + let elem = TextElement(text: text) + + children.append(elem) + } +} + +extension XMLElement: CustomStringConvertible { + /// The tag, attributes and content for a `XMLElement` instance (content) + public var description: String { + let attributesString = allAttributes.reduce("", { $0 + " " + $1.1.description }) + + if !children.isEmpty { + var xmlReturn = [String]() + xmlReturn.append("<\(name)\(attributesString)>") + for child in children { + xmlReturn.append(child.description) + } + xmlReturn.append("") + return xmlReturn.joined() + } + + return "<\(name)\(attributesString)>\(text)" + } +} + +/*: 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(by name: N) -> XMLAttribute? where N.RawValue == String { + attribute(by: name.rawValue) + } +} + +// Workaround for "'XMLElement' is ambiguous for type lookup in this context" error on macOS. +// +// 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 XMLHash { + public typealias XMLElement = XMLHashXMLElement +} + +public typealias XMLHashXMLElement = XMLElement diff --git a/Source/XMLHash.swift b/Source/XMLHash.swift index 91dca0de..3b507c7c 100644 --- a/Source/XMLHash.swift +++ b/Source/XMLHash.swift @@ -23,12 +23,6 @@ // THE SOFTWARE. // -// swiftlint exceptions: -// - Disabled file_length because there are a number of users that still pull the -// source down as is and it makes pulling the code into a project easier. - -// swiftlint:disable file_length - import Foundation #if canImport(FoundationXML) import FoundationXML @@ -215,548 +209,3 @@ extension XMLParserDelegate { } #endif - -/// 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 - @available(*, unavailable, renamed: "element(_:)") - public static func Element(_: XMLElement) -> XMLIndexer { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "list(_:)") - public static func List(_: [XMLElement]) -> XMLIndexer { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "stream(_:)") - public static func Stream(_: IndexOps) -> XMLIndexer { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "xmlError(_:)") - public static func XMLError(_: IndexingError) -> XMLIndexer { - fatalError("unavailable") - } - @available(*, unavailable, renamed: "withAttribute(_:_:)") - public static func withAttr(_ attr: String, _ value: String) throws -> XMLIndexer { - fatalError("unavailable") - } -// swiftlint:enable identifier_name - - /// The underlying XMLElement at the currently indexed level of XML. - public var element: XMLElement? { - switch self { - case .element(let elem): - return elem - case .stream(let ops): - let list = ops.findElements() - return list.element - default: - return nil - } - } - - /// 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): - return list - case .element(let elem): - return [elem] - case .stream(let ops): - let list = ops.findElements() - return list.allElements - default: - return [] - } - } - - /// All child elements from the currently indexed level - public var children: [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(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. - - - 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(_ attr: String, _ value: String) throws -> XMLIndexer { - switch self { - case .stream(let opStream): - let match = opStream.findElements() - return try match.withAttribute(attr, value) - case .list(let list): - if let elem = list.first(where: { - value.compare($0.attribute(by: attr)?.text, $0.caseInsensitive) - }) { - return .element(elem) - } - throw IndexingError.attributeValue(attr: attr, value: value) - case .element(let elem): - if value.compare(elem.attribute(by: attr)?.text, elem.caseInsensitive) { - return .element(elem) - } - throw IndexingError.attributeValue(attr: attr, value: value) - default: - throw IndexingError.attribute(attr: attr) - } - } - - /** - Initializes the XMLIndexer - - - parameter _: should be an instance of XMLElement, but supports other values for error handling - - throws: an Error if the object passed in isn't an XMLElement or LaxyXMLParser - */ - public init(_ rawObject: AnyObject) throws { - switch rawObject { - case let value as XMLElement: - self = .element(value) - case let value as LazyXMLParser: - self = .stream(IndexOps(parser: value)) - default: - throw IndexingError.initialize(instance: rawObject) - } - } - - /** - Initializes the XMLIndexer - - - parameter _: an instance of XMLElement - */ - public init(_ elem: XMLElement) { - self = .element(elem) - } - - init(_ stream: LazyXMLParser) { - self = .stream(IndexOps(parser: stream)) - } - - /** - Find an XML element at the current level by element name - - - 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(_ key: String) throws -> XMLIndexer { - switch self { - case .stream(let opStream): - let oper = IndexOp(key) - opStream.ops.append(oper) - return .stream(opStream) - case .element(let elem): - let match = elem.xmlChildren.filter({ - $0.name.compare(key, $0.caseInsensitive) - }) - if !match.isEmpty { - if match.count == 1 { - return .element(match[0]) - } else { - return .list(match) - } - } - throw IndexingError.key(key: key) - default: - throw IndexingError.key(key: key) - } - } - - /** - Find an XML element at the current level by element name - - - parameter key: The element name to index by - - returns: instance of XMLIndexer to match the element (or elements) found by - */ - public subscript(key: String) -> XMLIndexer { - do { - return try self.byKey(key) - } catch let error as IndexingError { - return .xmlError(error) - } catch { - return .xmlError(IndexingError.key(key: key)) - } - } - - /** - Find an XML element by index within a list of XML Elements at the current level - - - parameter index: The 0-based index to index by - - throws: XMLIndexer.XMLError if the index isn't found - - returns: instance of XMLIndexer to match the element (or elements) found by index - */ - public func byIndex(_ index: Int) throws -> XMLIndexer { - switch self { - case .stream(let opStream): - opStream.ops[opStream.ops.count - 1].index = index - return .stream(opStream) - case .list(let list): - if index < list.count { - return .element(list[index]) - } - return .xmlError(IndexingError.index(idx: index)) - case .element(let elem): - if index == 0 { - return .element(elem) - } - return .xmlError(IndexingError.index(idx: index)) - default: - return .xmlError(IndexingError.index(idx: index)) - } - } - - /** - Find an XML element by index - - - parameter index: The 0-based index to index by - - returns: instance of XMLIndexer to match the element (or elements) found by index - */ - public subscript(index: Int) -> XMLIndexer { - do { - return try byIndex(index) - } catch let error as IndexingError { - return .xmlError(error) - } catch { - return .xmlError(IndexingError.index(idx: index)) - } - } -} - -/// XMLIndexer extensions - -extension XMLIndexer: CustomStringConvertible { - /// The XML representation of the XMLIndexer at the current level - public var description: String { - switch self { - case .list(let list): - return list.reduce("", { $0 + $1.description }) - case .element(let elem): - if elem.name == rootElementName { - return elem.children.reduce("", { $0 + $1.description }) - } - - return elem.description - default: - return "" - } - } -} - -/// Models content for an XML doc, whether it is text or XML -public protocol XMLContent: CustomStringConvertible { } - -/// Models a text element -public class TextElement: XMLContent { - /// The underlying text value - public let text: String - - init(text: String) { - self.text = text - } -} - -public struct XMLAttribute { - public let name: String - public let text: String - - init(name: String, text: String) { - self.name = name - self.text = text - } -} - -/// Models an XML element, including name, text and attributes -public class XMLElement: XMLContent { - /// The name of the element - public let name: String - - /// 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 - } - return allAttributes[name] - } - - /// The inner text of the element, if it exists - public var text: String { - children.reduce("", { - if let element = $1 as? TextElement { - return $0 + element.text - } - - return $0 - }) - } - - /// The inner text of the element and its children - public var recursiveText: String { - children.reduce("", { - if let textElement = $1 as? TextElement { - return $0 + textElement.text - } else if let xmlElement = $1 as? XMLElement { - return $0 + xmlElement.recursiveText - } else { - return $0 - } - }) - } - - 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] { - children.compactMap { $0 as? XMLElement } - } - - /** - Initialize an XMLElement instance - - - 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, options: XMLHashOptions) { - self.name = name - self.index = index - self.options = options - } - - /** - Adds a new XMLElement underneath this instance of XMLElement - - - parameters: - - name: The name of the new element to be added - - withAttributes: The attributes dictionary for the element being added - - returns: The XMLElement that has now been added - */ - - func addElement(_ name: String, withAttributes attributes: [String: String], caseInsensitive: Bool) -> XMLElement { - let element = XMLElement(name: name, index: count, options: options) - count += 1 - - children.append(element) - - for (key, value) in attributes { - element.allAttributes[key] = XMLAttribute(name: key, text: value) - } - - return element - } - - func addText(_ text: String) { - let elem = TextElement(text: text) - - children.append(elem) - } -} - -extension TextElement: CustomStringConvertible { - /// The text value for a `TextElement` instance. - public var description: String { - text - } -} - -extension XMLAttribute: CustomStringConvertible { - /// The textual representation of an `XMLAttribute` instance. - public var description: String { - "\(name)=\"\(text)\"" - } -} - -extension XMLElement: CustomStringConvertible { - /// The tag, attributes and content for a `XMLElement` instance (content) - public var description: String { - let attributesString = allAttributes.reduce("", { $0 + " " + $1.1.description }) - - if !children.isEmpty { - var xmlReturn = [String]() - xmlReturn.append("<\(name)\(attributesString)>") - for child in children { - xmlReturn.append(child.description) - } - xmlReturn.append("") - return xmlReturn.joined() - } - - return "<\(name)\(attributesString)>\(text)" - } -} - -// Workaround for "'XMLElement' is ambiguous for type lookup in this context" error on macOS. -// -// 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 XMLHash { - public typealias XMLElement = XMLHashXMLElement -} - -public typealias XMLHashXMLElement = XMLElement - -fileprivate extension String { - func compare(_ str2: String?, _ insensitive: Bool) -> Bool { - guard let str2 = str2 else { - return false - } - let str1 = self - if insensitive { - return str1.caseInsensitiveCompare(str2) == .orderedSame - } - 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(_ 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(_ 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(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(by name: N) -> XMLAttribute? where N.RawValue == String { - attribute(by: name.rawValue) - } -} diff --git a/Source/XMLIndexer.swift b/Source/XMLIndexer.swift new file mode 100644 index 00000000..e21529de --- /dev/null +++ b/Source/XMLIndexer.swift @@ -0,0 +1,364 @@ +// +// XMLIndexer.swift +// SWXMLHash +// +// Copyright (c) 2022 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. +// + +import Foundation + +/// 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 + @available(*, unavailable, renamed: "element(_:)") + public static func Element(_: XMLElement) -> XMLIndexer { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "list(_:)") + public static func List(_: [XMLElement]) -> XMLIndexer { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "stream(_:)") + public static func Stream(_: IndexOps) -> XMLIndexer { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "xmlError(_:)") + public static func XMLError(_: IndexingError) -> XMLIndexer { + fatalError("unavailable") + } + @available(*, unavailable, renamed: "withAttribute(_:_:)") + public static func withAttr(_ attr: String, _ value: String) throws -> XMLIndexer { + fatalError("unavailable") + } +// swiftlint:enable identifier_name + + /// The underlying XMLElement at the currently indexed level of XML. + public var element: XMLElement? { + switch self { + case .element(let elem): + return elem + case .stream(let ops): + let list = ops.findElements() + return list.element + default: + return nil + } + } + + /// 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): + return list + case .element(let elem): + return [elem] + case .stream(let ops): + let list = ops.findElements() + return list.allElements + default: + return [] + } + } + + /// All child elements from the currently indexed level + public var children: [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(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. + + - 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(_ attr: String, _ value: String) throws -> XMLIndexer { + switch self { + case .stream(let opStream): + let match = opStream.findElements() + return try match.withAttribute(attr, value) + case .list(let list): + if let elem = list.first(where: { + value.compare($0.attribute(by: attr)?.text, $0.caseInsensitive) + }) { + return .element(elem) + } + throw IndexingError.attributeValue(attr: attr, value: value) + case .element(let elem): + if value.compare(elem.attribute(by: attr)?.text, elem.caseInsensitive) { + return .element(elem) + } + throw IndexingError.attributeValue(attr: attr, value: value) + default: + throw IndexingError.attribute(attr: attr) + } + } + + /** + Initializes the XMLIndexer + + - parameter _: should be an instance of XMLElement, but supports other values for error handling + - throws: an Error if the object passed in isn't an XMLElement or LaxyXMLParser + */ + public init(_ rawObject: AnyObject) throws { + switch rawObject { + case let value as XMLElement: + self = .element(value) + case let value as LazyXMLParser: + self = .stream(IndexOps(parser: value)) + default: + throw IndexingError.initialize(instance: rawObject) + } + } + + /** + Initializes the XMLIndexer + + - parameter _: an instance of XMLElement + */ + public init(_ elem: XMLElement) { + self = .element(elem) + } + + init(_ stream: LazyXMLParser) { + self = .stream(IndexOps(parser: stream)) + } + + /** + Find an XML element at the current level by element name + + - 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(_ key: String) throws -> XMLIndexer { + switch self { + case .stream(let opStream): + let oper = IndexOp(key) + opStream.ops.append(oper) + return .stream(opStream) + case .element(let elem): + let match = elem.xmlChildren.filter({ + $0.name.compare(key, $0.caseInsensitive) + }) + if !match.isEmpty { + if match.count == 1 { + return .element(match[0]) + } else { + return .list(match) + } + } + throw IndexingError.key(key: key) + default: + throw IndexingError.key(key: key) + } + } + + /** + Find an XML element at the current level by element name + + - parameter key: The element name to index by + - returns: instance of XMLIndexer to match the element (or elements) found by + */ + public subscript(key: String) -> XMLIndexer { + do { + return try self.byKey(key) + } catch let error as IndexingError { + return .xmlError(error) + } catch { + return .xmlError(IndexingError.key(key: key)) + } + } + + /** + Find an XML element by index within a list of XML Elements at the current level + + - parameter index: The 0-based index to index by + - throws: XMLIndexer.XMLError if the index isn't found + - returns: instance of XMLIndexer to match the element (or elements) found by index + */ + public func byIndex(_ index: Int) throws -> XMLIndexer { + switch self { + case .stream(let opStream): + opStream.ops[opStream.ops.count - 1].index = index + return .stream(opStream) + case .list(let list): + if index < list.count { + return .element(list[index]) + } + return .xmlError(IndexingError.index(idx: index)) + case .element(let elem): + if index == 0 { + return .element(elem) + } + return .xmlError(IndexingError.index(idx: index)) + default: + return .xmlError(IndexingError.index(idx: index)) + } + } + + /** + Find an XML element by index + + - parameter index: The 0-based index to index by + - returns: instance of XMLIndexer to match the element (or elements) found by index + */ + public subscript(index: Int) -> XMLIndexer { + do { + return try byIndex(index) + } catch let error as IndexingError { + return .xmlError(error) + } catch { + return .xmlError(IndexingError.index(idx: index)) + } + } +} + +/// XMLIndexer extensions + +extension XMLIndexer: CustomStringConvertible { + /// The XML representation of the XMLIndexer at the current level + public var description: String { + switch self { + case .list(let list): + return list.reduce("", { $0 + $1.description }) + case .element(let elem): + if elem.name == rootElementName { + return elem.children.reduce("", { $0 + $1.description }) + } + + return elem.description + default: + return "" + } + } +} + +/*: 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(_ 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(_ 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(key: K) -> XMLIndexer where K.RawValue == String { + self[key.rawValue] + } +} From 821f8c79117b285d501c2c7fce7e68ae83a6579b Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 19:47:13 -0500 Subject: [PATCH 12/16] chore: minor tweaks and cleanup Including fixes to docs, punctuation, naming, etc. --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 8 +++---- SWXMLHash.xcodeproj/project.pbxproj | 16 ++++++------- .../XMLIndexer+XMLIndexerDeserializable.swift | 4 ++-- Source/FullXMLParser.swift | 2 +- Source/IndexOp.swift | 2 +- Source/LazyXMLParser.swift | 2 +- Source/XMLElement.swift | 2 +- Source/XMLIndexer.swift | 6 ++--- Tests/LinuxMain.swift | 2 +- ...versionArrayOfNonPrimitiveTypesTests.swift | 24 +++++++++---------- .../TypeConversionBasicTypesTests.swift | 8 +++---- .../TypeConversionComplexTypesTests.swift | 8 +++---- ...> TypeConversionPrimitiveTypesTests.swift} | 8 +++---- 15 files changed, 48 insertions(+), 48 deletions(-) rename Tests/SWXMLHashTests/{TypeConversionPrimitypeTypesTests.swift => TypeConversionPrimitiveTypesTests.swift} (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8ca8fda..1f8c36ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ ## v4.8.0 (March 6, 2019) -- Added support for using String backed RawRepresentables in place of Strings for revelant APIs (via +- Added support for using String backed RawRepresentables in place of Strings for relevant APIs (via [#204](https://github.com/drmohundro/SWXMLHash/pull/207)) ## v4.7.6 (December 11, 2018) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f60c1e55..6be66b65 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ I monitor [StackOverflow](http://stackoverflow.com) under the [SWXMLHash tag](ht When reporting issues, please include: * Which version of Xcode you're using -* Which OS or platform you're targetting +* Which OS or platform you're targeting * Any stack trace or compiler error * Code snippets that reproduce the behavior diff --git a/README.md b/README.md index 4970f307..ebfdccf0 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ dependencies: [ ### Manual Installation To install manually, you'll need to clone the SWXMLHash repository. You can do -this in a separate directory or you can make use of git submodules - in this +this in a separate directory, or you can make use of git submodules - in this case, git submodules are recommended so that your repository has details about which commit of SWXMLHash you're using. Once this is done, you can just drop the `SWXMLHash.swift` file into your project. @@ -134,8 +134,8 @@ The available options at this time are: will be returned as "\") - Defaults to `false` - `caseInsensitive` - - This setting allows for key lookups to be case insensitive. Typically XML is - a case sensitive language, but this option lets you bypass this if + - This setting allows for key lookups to be case-insensitive. Typically, XML is + a case-sensitive language, but this option lets you bypass this if necessary. - Defaults to `false` - `encoding` @@ -349,7 +349,7 @@ Given: ``` -The following will return return "Midnight Rain". Filtering can be by any part +The following will return "Midnight Rain". Filtering can be by any part of the `XMLElement` class or by index as well. ```swift diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index 9cbf1ce9..396acd6d 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -146,9 +146,9 @@ CDC6D13B1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13A1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift */; }; CDC6D13C1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13A1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift */; }; CDC6D13D1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13A1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift */; }; - CDC6D13F1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */; }; - CDC6D1401D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */; }; - CDC6D1411D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */; }; + CDC6D13F1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift */; }; + CDC6D1401D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift */; }; + CDC6D1411D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift */; }; CDC6D1431D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D1421D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift */; }; CDC6D1441D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D1421D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift */; }; CDC6D1451D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6D1421D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift */; }; @@ -263,7 +263,7 @@ CDC6D1321D32D7C300570DE5 /* LazyTypesConversionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LazyTypesConversionTests.swift; sourceTree = ""; }; CDC6D1361D32D7F400570DE5 /* TypeConversionBasicTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeConversionBasicTypesTests.swift; sourceTree = ""; }; CDC6D13A1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeConversionComplexTypesTests.swift; sourceTree = ""; }; - CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeConversionPrimitypeTypesTests.swift; sourceTree = ""; }; + CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeConversionPrimitiveTypesTests.swift; sourceTree = ""; }; CDC6D1421D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeConversionArrayOfNonPrimitiveTypesTests.swift; sourceTree = ""; }; CDDEC74C1BF6311A00AB138B /* SWXMLHash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWXMLHash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CDDEC7551BF6311B00AB138B /* SWXMLHash tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SWXMLHash tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -443,7 +443,7 @@ CDC6D1421D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift */, CDC6D1361D32D7F400570DE5 /* TypeConversionBasicTypesTests.swift */, CDC6D13A1D32D84900570DE5 /* TypeConversionComplexTypesTests.swift */, - CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift */, + CDC6D13E1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift */, CDC6D11E1D32D70800570DE5 /* WhiteSpaceParsingTests.swift */, CDC6D11A1D32D6CE00570DE5 /* XMLParsingTests.swift */, E25A0775261CF79300C68B90 /* XMLParsingValidationTests.swift */, @@ -805,7 +805,7 @@ CDC6D1271D32D76400570DE5 /* LazyXMLParsingTests.swift in Sources */, CDC6D12F1D32D79F00570DE5 /* XMLHashConfigTests.swift in Sources */, 6C42BED1205183A100137D31 /* shim.swift in Sources */, - CDC6D13F1D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */, + CDC6D13F1D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */, E25A0776261CF79300C68B90 /* XMLParsingValidationTests.swift in Sources */, CDC6D1431D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */, CDC6D11F1D32D70800570DE5 /* WhiteSpaceParsingTests.swift in Sources */, @@ -858,7 +858,7 @@ CDC6D1281D32D76400570DE5 /* LazyXMLParsingTests.swift in Sources */, CDC6D1301D32D79F00570DE5 /* XMLHashConfigTests.swift in Sources */, 6C42BED2205183A100137D31 /* shim.swift in Sources */, - CDC6D1401D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */, + CDC6D1401D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */, E25A0777261CF79300C68B90 /* XMLParsingValidationTests.swift in Sources */, CDC6D1441D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */, CDC6D1201D32D70800570DE5 /* WhiteSpaceParsingTests.swift in Sources */, @@ -911,7 +911,7 @@ CDC6D1291D32D76400570DE5 /* LazyXMLParsingTests.swift in Sources */, CDC6D1311D32D79F00570DE5 /* XMLHashConfigTests.swift in Sources */, 6C42BED3205183A100137D31 /* shim.swift in Sources */, - CDC6D1411D32D98400570DE5 /* TypeConversionPrimitypeTypesTests.swift in Sources */, + CDC6D1411D32D98400570DE5 /* TypeConversionPrimitiveTypesTests.swift in Sources */, E25A0778261CF79300C68B90 /* XMLParsingValidationTests.swift in Sources */, CDC6D1451D32D9D200570DE5 /* TypeConversionArrayOfNonPrimitiveTypesTests.swift in Sources */, CDC6D1211D32D70800570DE5 /* WhiteSpaceParsingTests.swift in Sources */, diff --git a/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift b/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift index 96ddfb90..4bdb1056 100644 --- a/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift @@ -481,7 +481,7 @@ extension XMLElement { - returns: The deserialized `T` value */ public func value(ofAttribute attr: String) throws -> T { - if let attr = self.attribute(by: attr) { + if let attr = attribute(by: attr) { let deserialized = try T.deserialize(attr) try deserialized.validate() return deserialized @@ -497,7 +497,7 @@ extension XMLElement { - returns: The deserialized `T?` value, or nil if the attribute does not exist. */ public func value(ofAttribute attr: String) -> T? { - if let attr = self.attribute(by: attr) { + if let attr = attribute(by: attr) { let deserialized = try? T.deserialize(attr) if deserialized != nil { try? deserialized?.validate() diff --git a/Source/FullXMLParser.swift b/Source/FullXMLParser.swift index 55a3c03d..2b5cf724 100644 --- a/Source/FullXMLParser.swift +++ b/Source/FullXMLParser.swift @@ -64,7 +64,7 @@ class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { attributes attributeDict: [String: String]) { let currentNode = parentStack .top() - .addElement(elementName, withAttributes: attributeDict, caseInsensitive: self.options.caseInsensitive) + .addElement(elementName, withAttributes: attributeDict, caseInsensitive: options.caseInsensitive) parentStack.push(currentNode) } diff --git a/Source/IndexOp.swift b/Source/IndexOp.swift index 6c36947e..ebe9a508 100644 --- a/Source/IndexOp.swift +++ b/Source/IndexOp.swift @@ -32,7 +32,7 @@ public class IndexOp { init(_ key: String) { self.key = key - self.index = -1 + index = -1 } func toString() -> String { diff --git a/Source/LazyXMLParser.swift b/Source/LazyXMLParser.swift index 148cf4f9..dcd8e1ff 100644 --- a/Source/LazyXMLParser.swift +++ b/Source/LazyXMLParser.swift @@ -72,7 +72,7 @@ class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { let currentNode = parentStack .top() - .addElement(elementName, withAttributes: attributeDict, caseInsensitive: self.options.caseInsensitive) + .addElement(elementName, withAttributes: attributeDict, caseInsensitive: options.caseInsensitive) parentStack.push(currentNode) } diff --git a/Source/XMLElement.swift b/Source/XMLElement.swift index ca468f6f..1017eaf5 100644 --- a/Source/XMLElement.swift +++ b/Source/XMLElement.swift @@ -171,7 +171,7 @@ extension XMLElement { // // 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. +// Following code allow to using `SWXMLHash.XMLElement` in client codes. extension XMLHash { public typealias XMLElement = XMLHashXMLElement } diff --git a/Source/XMLIndexer.swift b/Source/XMLIndexer.swift index e21529de..d977ec69 100644 --- a/Source/XMLIndexer.swift +++ b/Source/XMLIndexer.swift @@ -111,7 +111,7 @@ public enum XMLIndexer { public func filterChildren(_ included: (_ elem: XMLElement, _ index: Int) -> Bool) -> XMLIndexer { let children = handleFilteredResults(list: childElements, included: included) - if let current = self.element { + if let current = element { let filteredElem = XMLElement(name: current.name, index: current.index, options: current.options) filteredElem.children = children.allElements return .element(filteredElem) @@ -176,7 +176,7 @@ public enum XMLIndexer { Initializes the XMLIndexer - parameter _: should be an instance of XMLElement, but supports other values for error handling - - throws: an Error if the object passed in isn't an XMLElement or LaxyXMLParser + - throws: an Error if the object passed in isn't an XMLElement or LazyXMLParser */ public init(_ rawObject: AnyObject) throws { switch rawObject { @@ -240,7 +240,7 @@ public enum XMLIndexer { */ public subscript(key: String) -> XMLIndexer { do { - return try self.byKey(key) + return try byKey(key) } catch let error as IndexingError { return .xmlError(error) } catch { diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 4a514ab3..bd9ab170 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -36,7 +36,7 @@ XCTMain([ testCase(TypeConversionArrayOfNonPrimitiveTypesTests.allTests), testCase(TypeConversionBasicTypesTests.allTests), testCase(TypeConversionComplexTypesTests.allTests), - testCase(TypeConversionPrimitypeTypesTests.allTests), + testCase(TypeConversionPrimitiveTypesTests.allTests), testCase(WhiteSpaceParsingTests.allTests), testCase(XMLParsingTests.allTests) ]) diff --git a/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift index 8f0076c9..4f22116a 100644 --- a/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionArrayOfNonPrimitiveTypesTests.swift @@ -91,7 +91,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { parser = XMLHash.parse(xmlWithArraysOfTypes) } - func testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional() { + func testShouldConvertArrayOfGoodBasicItemsToNonOptional() { do { let value: [BasicItem] = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() XCTAssertEqual(value, correctBasicItems) @@ -100,7 +100,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } - func testShouldConvertArrayOfGoodBasicitemsItemsToOptional() { + func testShouldConvertArrayOfGoodBasicItemsToOptional() { do { let value: [BasicItem]? = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() XCTAssertNotNil(value) @@ -112,7 +112,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } - func testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals() { + func testShouldConvertArrayOfGoodBasicItemsToArrayOfOptionals() { do { let value: [BasicItem?] = try parser!["root"]["arrayOfGoodBasicItems"]["basicItem"].value() XCTAssertEqual(value.compactMap({ $0 }), correctBasicItems) @@ -121,7 +121,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } - func testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional() { + func testShouldThrowWhenConvertingArrayOfBadBasicItemsToNonOptional() { XCTAssertThrowsError(try (parser!["root"]["arrayOfBadBasicItems"]["basicItem"].value() as [BasicItem])) { error in guard error is XMLDeserializationError else { XCTFail("Wrong type of error") @@ -130,7 +130,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } - func testShouldThrowWhenConvertingArrayOfBadBasicitemsToOptional() { + func testShouldThrowWhenConvertingArrayOfBadBasicItemsToOptional() { XCTAssertThrowsError(try (parser!["root"]["arrayOfBadBasicItems"]["basicItem"].value() as [BasicItem]?)) { error in guard error is XMLDeserializationError else { XCTFail("Wrong type of error") @@ -139,7 +139,7 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { } } - func testShouldThrowWhenConvertingArrayOfBadBasicitemsToArrayOfOptionals() { + func testShouldThrowWhenConvertingArrayOfBadBasicItemsToArrayOfOptionals() { XCTAssertThrowsError(try (parser!["root"]["arrayOfBadBasicItems"]["basicItem"].value() as [BasicItem?])) { error in guard error is XMLDeserializationError else { XCTFail("Wrong type of error") @@ -262,12 +262,12 @@ class TypeConversionArrayOfNonPrimitiveTypesTests: XCTestCase { extension TypeConversionArrayOfNonPrimitiveTypesTests { static var allTests: [(String, (TypeConversionArrayOfNonPrimitiveTypesTests) -> () throws -> Void)] { [ - ("testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional", testShouldConvertArrayOfGoodBasicitemsItemsToNonOptional), - ("testShouldConvertArrayOfGoodBasicitemsItemsToOptional", testShouldConvertArrayOfGoodBasicitemsItemsToOptional), - ("testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals", testShouldConvertArrayOfGoodBasicitemsItemsToArrayOfOptionals), - ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional", testShouldThrowWhenConvertingArrayOfBadBasicitemsToNonOptional), - ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToOptional", testShouldThrowWhenConvertingArrayOfBadBasicitemsToOptional), - ("testShouldThrowWhenConvertingArrayOfBadBasicitemsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfBadBasicitemsToArrayOfOptionals), + ("testShouldConvertArrayOfGoodBasicItemsToNonOptional", testShouldConvertArrayOfGoodBasicItemsToNonOptional), + ("testShouldConvertArrayOfGoodBasicItemsToOptional", testShouldConvertArrayOfGoodBasicItemsToOptional), + ("testShouldConvertArrayOfGoodBasicItemsToArrayOfOptionals", testShouldConvertArrayOfGoodBasicItemsToArrayOfOptionals), + ("testShouldThrowWhenConvertingArrayOfBadBasicItemsToNonOptional", testShouldThrowWhenConvertingArrayOfBadBasicItemsToNonOptional), + ("testShouldThrowWhenConvertingArrayOfBadBasicItemsToOptional", testShouldThrowWhenConvertingArrayOfBadBasicItemsToOptional), + ("testShouldThrowWhenConvertingArrayOfBadBasicItemsToArrayOfOptionals", testShouldThrowWhenConvertingArrayOfBadBasicItemsToArrayOfOptionals), ("testShouldConvertArrayOfEmptyMissingToOptional", testShouldConvertArrayOfEmptyMissingToOptional), ("testShouldConvertArrayOfGoodAttributeItemsToNonOptional", testShouldConvertArrayOfGoodAttributeItemsToNonOptional), ("testShouldConvertArrayOfGoodAttributeItemsToOptional", testShouldConvertArrayOfGoodAttributeItemsToOptional), diff --git a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift index 88b8181c..f67c7759 100644 --- a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift @@ -494,7 +494,7 @@ class TypeConversionBasicTypesTests: XCTestCase { let correctBasicItem = BasicItem(name: "the name of basic item", price: 99.14, id: "1234") - func testBasicItemShouldConvertBasicitemToNonOptional() { + func testBasicItemShouldConvertBasicItemToNonOptional() { do { let value: BasicItem = try parser!["root"]["basicItem"].value() XCTAssertEqual(value, correctBasicItem) @@ -523,7 +523,7 @@ class TypeConversionBasicTypesTests: XCTestCase { } } - func testBasicItemShouldConvertBasicitemToOptional() { + func testBasicItemShouldConvertBasicItemToOptional() { do { let value: BasicItem? = try parser!["root"]["basicItem"].value() XCTAssertEqual(value, correctBasicItem) @@ -755,10 +755,10 @@ extension TypeConversionBasicTypesTests { ("testBoolShouldConvertMissingToOptional", testBoolShouldConvertMissingToOptional), ("testBoolShouldConvertAttributeToNonOptional", testBoolShouldConvertAttributeToNonOptional), ("testBoolShouldConvertAttributeToOptional", testBoolShouldConvertAttributeToOptional), - ("testBasicItemShouldConvertBasicitemToNonOptional", testBasicItemShouldConvertBasicitemToNonOptional), + ("testBasicItemShouldConvertBasicItemToNonOptional", testBasicItemShouldConvertBasicItemToNonOptional), ("testBasicItemShouldThrowWhenConvertingEmptyToNonOptional", testBasicItemShouldThrowWhenConvertingEmptyToNonOptional), ("testBasicItemShouldThrowWhenConvertingMissingToNonOptional", testBasicItemShouldThrowWhenConvertingMissingToNonOptional), - ("testBasicItemShouldConvertBasicitemToOptional", testBasicItemShouldConvertBasicitemToOptional), + ("testBasicItemShouldConvertBasicItemToOptional", testBasicItemShouldConvertBasicItemToOptional), ("testBasicItemShouldConvertEmptyToOptional", testBasicItemShouldConvertEmptyToOptional), ("testBasicItemShouldConvertMissingToOptional", testBasicItemShouldConvertMissingToOptional), ("testAttributeItemShouldConvertAttributeItemToNonOptional", testAttributeItemShouldConvertAttributeItemToNonOptional), diff --git a/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift index e73bb179..0739e10e 100644 --- a/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift @@ -79,7 +79,7 @@ class TypeConversionComplexTypesTests: XCTestCase { parser = XMLHash.parse(xmlWithComplexType) } - func testShouldConvertComplexitemToNonOptional() { + func testShouldConvertComplexItemToNonOptional() { do { let value: ComplexItem = try parser!["root"]["complexItem"].value() XCTAssertEqual(value, correctComplexItem) @@ -106,7 +106,7 @@ class TypeConversionComplexTypesTests: XCTestCase { } } - func testShouldConvertComplexitemToOptional() { + func testShouldConvertComplexItemToOptional() { do { let value: ComplexItem? = try parser!["root"]["complexItem"].value() XCTAssertEqual(value, correctComplexItem) @@ -159,10 +159,10 @@ extension ComplexItem: Equatable { extension TypeConversionComplexTypesTests { static var allTests: [(String, (TypeConversionComplexTypesTests) -> () throws -> Void)] { [ - ("testShouldConvertComplexitemToNonOptional", testShouldConvertComplexitemToNonOptional), + ("testShouldConvertComplexItemToNonOptional", testShouldConvertComplexItemToNonOptional), ("testShouldThrowWhenConvertingEmptyToNonOptional", testShouldThrowWhenConvertingEmptyToNonOptional), ("testShouldThrowWhenConvertingMissingToNonOptional", testShouldThrowWhenConvertingMissingToNonOptional), - ("testShouldConvertComplexitemToOptional", testShouldConvertComplexitemToOptional), + ("testShouldConvertComplexItemToOptional", testShouldConvertComplexItemToOptional), ("testShouldConvertEmptyToOptional", testShouldConvertEmptyToOptional), ("testShouldConvertMissingToOptional", testShouldConvertMissingToOptional) ] diff --git a/Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionPrimitiveTypesTests.swift similarity index 98% rename from Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift rename to Tests/SWXMLHashTests/TypeConversionPrimitiveTypesTests.swift index ef1546ed..bda885b3 100644 --- a/Tests/SWXMLHashTests/TypeConversionPrimitypeTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionPrimitiveTypesTests.swift @@ -1,5 +1,5 @@ // -// TypeConversionPrimitypeTypesTests.swift +// TypeConversionPrimitiveTypesTests.swift // SWXMLHash // // Copyright (c) 2016 David Mohundro @@ -28,7 +28,7 @@ import XCTest // swiftlint:disable line_length -class TypeConversionPrimitypeTypesTests: XCTestCase { +class TypeConversionPrimitiveTypesTests: XCTestCase { var parser: XMLIndexer? let xmlWithArraysOfTypes = """ @@ -234,8 +234,8 @@ class TypeConversionPrimitypeTypesTests: XCTestCase { } } -extension TypeConversionPrimitypeTypesTests { - static var allTests: [(String, (TypeConversionPrimitypeTypesTests) -> () throws -> Void)] { +extension TypeConversionPrimitiveTypesTests { + static var allTests: [(String, (TypeConversionPrimitiveTypesTests) -> () throws -> Void)] { [ ("testShouldConvertArrayOfGoodIntsToNonOptional", testShouldConvertArrayOfGoodIntsToNonOptional), ("testShouldConvertArrayOfGoodIntsToOptional", testShouldConvertArrayOfGoodIntsToOptional), From 62774b3d0c89bdf9150d08f47c2fd8e01c426a88 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 19:57:42 -0500 Subject: [PATCH 13/16] fix: correct usage of FoundationXML --- Source/FullXMLParser.swift | 4 ++ Source/LazyXMLParser.swift | 4 ++ Source/XMLHash.swift | 83 -------------------------------------- 3 files changed, 8 insertions(+), 83 deletions(-) diff --git a/Source/FullXMLParser.swift b/Source/FullXMLParser.swift index 2b5cf724..99cfaf7e 100644 --- a/Source/FullXMLParser.swift +++ b/Source/FullXMLParser.swift @@ -25,6 +25,10 @@ import Foundation +#if os(Linux) +import FoundationXML +#endif + /// The implementation of XMLParserDelegate and where the parsing actually happens. class FullXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { required init(_ options: XMLHashOptions) { diff --git a/Source/LazyXMLParser.swift b/Source/LazyXMLParser.swift index dcd8e1ff..c074f438 100644 --- a/Source/LazyXMLParser.swift +++ b/Source/LazyXMLParser.swift @@ -25,6 +25,10 @@ import Foundation +#if os(Linux) +import FoundationXML +#endif + /// The implementation of XMLParserDelegate and where the lazy parsing actually happens. class LazyXMLParser: NSObject, SimpleXmlParser, XMLParserDelegate { required init(_ options: XMLHashOptions) { diff --git a/Source/XMLHash.swift b/Source/XMLHash.swift index 3b507c7c..5ba3c403 100644 --- a/Source/XMLHash.swift +++ b/Source/XMLHash.swift @@ -126,86 +126,3 @@ protocol SimpleXmlParser { init(_ options: XMLHashOptions) func parse(_ data: Data) -> XMLIndexer } - -#if os(Linux) - -extension XMLParserDelegate { - func parserDidStartDocument(_ parser: XMLParser) { } - func parserDidEndDocument(_ parser: XMLParser) { } - - func parser(_ parser: XMLParser, - foundNotationDeclarationWithName name: String, - publicID: String?, - systemID: String?) { } - - func parser(_ parser: XMLParser, - foundUnparsedEntityDeclarationWithName name: String, - publicID: String?, - systemID: String?, - notationName: String?) { } - - func parser(_ parser: XMLParser, - foundAttributeDeclarationWithName attributeName: String, - forElement elementName: String, - type: String?, - defaultValue: String?) { } - - func parser(_ parser: XMLParser, - foundElementDeclarationWithName elementName: String, - model: String) { } - - func parser(_ parser: XMLParser, - foundInternalEntityDeclarationWithName name: String, - value: String?) { } - - func parser(_ parser: XMLParser, - foundExternalEntityDeclarationWithName name: String, - publicID: String?, - systemID: String?) { } - - func parser(_ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String]) { } - - func parser(_ parser: XMLParser, - didEndElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?) { } - - func parser(_ parser: XMLParser, - didStartMappingPrefix prefix: String, - toURI namespaceURI: String) { } - - func parser(_ parser: XMLParser, - didEndMappingPrefix prefix: String) { } - - func parser(_ parser: XMLParser, - foundCharacters string: String) { } - - func parser(_ parser: XMLParser, - foundIgnorableWhitespace whitespaceString: String) { } - - func parser(_ parser: XMLParser, - foundProcessingInstructionWithTarget target: String, - data: String?) { } - - func parser(_ parser: XMLParser, - foundComment comment: String) { } - - func parser(_ parser: XMLParser, - foundCDATA CDATABlock: Data) { } - - func parser(_ parser: XMLParser, - resolveExternalEntityName name: String, - systemID: String?) -> Data? { nil } - - func parser(_ parser: XMLParser, - parseErrorOccurred parseError: Error) { } - - func parser(_ parser: XMLParser, - validationErrorOccurred validationError: Error) { } -} - -#endif From e9543c077a5c66f1b9e32fda87ab355bd57385e9 Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 20:14:21 -0500 Subject: [PATCH 14/16] refactor: rename deserialization protocols --- README.md | 8 +-- SWXMLHash.xcodeproj/project.pbxproj | 60 +++++++++---------- .../Contents.swift | 2 +- ...ft => XMLIndexer+XMLDeserialization.swift} | 12 ++-- ...e.swift => XMLObjectDeserialization.swift} | 4 +- ...on.swift => XMLValueDeserialization.swift} | 4 +- .../Bool+XMLDeserialization.swift | 2 +- .../Double+XMLDeserialization.swift | 2 +- .../Float+XMLDeserialization.swift | 2 +- .../Int+XMLDeserialization.swift | 2 +- .../String+XMLDeserialization.swift | 2 +- .../TypeConversionBasicTypesTests.swift | 2 +- .../TypeConversionComplexTypesTests.swift | 2 +- 13 files changed, 52 insertions(+), 52 deletions(-) rename Source/Deserialization/{XMLIndexer+XMLIndexerDeserializable.swift => XMLIndexer+XMLDeserialization.swift} (98%) rename Source/Deserialization/{XMLIndexerDeserializable.swift => XMLObjectDeserialization.swift} (96%) rename Source/Deserialization/{XMLDeserialization.swift => XMLValueDeserialization.swift} (95%) diff --git a/README.md b/README.md index ebfdccf0..6307f941 100644 --- a/README.md +++ b/README.md @@ -325,20 +325,20 @@ Given: ```xml - + Gambardella, Matthew XML Developer's Guide Computer44.95 2000-10-01 - + Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 - + Corets, Eva Maeve Ascendant Fantasy @@ -402,7 +402,7 @@ With the following implementation for `Date` element and attribute deserialization: ```swift -extension Date: XMLElementDeserializable, XMLAttributeDeserializable { +extension Date: XMLDeserialization { public static func deserialize(_ element: XMLElement) throws -> Date { let date = stringToDate(element.text) diff --git a/SWXMLHash.xcodeproj/project.pbxproj b/SWXMLHash.xcodeproj/project.pbxproj index 396acd6d..6e4af1d8 100644 --- a/SWXMLHash.xcodeproj/project.pbxproj +++ b/SWXMLHash.xcodeproj/project.pbxproj @@ -7,10 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; - 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; - 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; - 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */; }; + 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */; }; + 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */; }; + 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */; }; + 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */; }; 5AF0E03C7AC0B144A711143C /* XMLElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */; }; 5AF0E228F0D4FD1B85A66B5E /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; 5AF0E2C961494B70E578CFF2 /* XMLContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */; }; @@ -43,10 +43,10 @@ 63ED3C092821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; 63ED3C0A2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; 63ED3C0B2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */; }; - 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; - 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; - 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; - 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */; }; + 63ED3C0C2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */; }; + 63ED3C0D2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */; }; + 63ED3C0E2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */; }; + 63ED3C0F2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */; }; 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */; }; @@ -59,10 +59,10 @@ 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */; }; - 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; - 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; - 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; - 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */; }; + 63ED3C1C2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */; }; + 63ED3C1D2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */; }; + 63ED3C1E2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */; }; + 63ED3C1F2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */; }; 63ED3C202821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; 63ED3C212821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; 63ED3C222821A723006A8A08 /* Bool+XMLDeserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */; }; @@ -217,7 +217,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLIndexerDeserializable.swift"; sourceTree = ""; }; + 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XMLIndexer+XMLDeserialization.swift"; sourceTree = ""; }; 5AF0E09CA8C0AAEABBD56BB4 /* XMLElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLElement.swift; sourceTree = ""; }; 5AF0E710E1B7BA748FBC986B /* TextElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextElement.swift; sourceTree = ""; }; 5AF0EADFD21AC848F6BECA1D /* XMLContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLContent.swift; sourceTree = ""; }; @@ -226,11 +226,11 @@ 5AF0EF51D084FE1024D1CE0D /* XMLAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLAttribute.swift; sourceTree = ""; }; 6317F1A5282179E200F6C364 /* Float+XMLDeserialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C002821A723006A8A08 /* String+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+XMLDeserialization.swift"; sourceTree = ""; }; - 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLDeserialization.swift; sourceTree = ""; }; + 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLValueDeserialization.swift; sourceTree = ""; }; 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLAttributeDeserializable.swift; sourceTree = ""; }; 63ED3C032821A723006A8A08 /* Double+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLElementDeserializable.swift; sourceTree = ""; }; - 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLIndexerDeserializable.swift; sourceTree = ""; }; + 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLObjectDeserialization.swift; sourceTree = ""; }; 63ED3C062821A723006A8A08 /* Bool+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C072821A723006A8A08 /* Int+XMLDeserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+XMLDeserialization.swift"; sourceTree = ""; }; 63ED3C282821AF4D006A8A08 /* XMLHashOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLHashOptions.swift; sourceTree = ""; }; @@ -353,10 +353,10 @@ isa = PBXGroup; children = ( 63ED3C022821A723006A8A08 /* XMLAttributeDeserializable.swift */, - 63ED3C012821A723006A8A08 /* XMLDeserialization.swift */, + 63ED3C012821A723006A8A08 /* XMLValueDeserialization.swift */, 63ED3C042821A723006A8A08 /* XMLElementDeserializable.swift */, - 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift */, - 63ED3C052821A723006A8A08 /* XMLIndexerDeserializable.swift */, + 54B83CC41C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift */, + 63ED3C052821A723006A8A08 /* XMLObjectDeserialization.swift */, ); path = Deserialization; sourceTree = ""; @@ -765,7 +765,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63ED3C1C2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C1C2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */, 63ED3C102821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C142821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C662821D247006A8A08 /* FullXMLParser.swift in Sources */, @@ -776,12 +776,12 @@ 63ED3C292821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C182821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD60840C196CA11D000B4F8D /* XMLHash.swift in Sources */, - 63ED3C0C2821A723006A8A08 /* XMLDeserialization.swift in Sources */, + 63ED3C0C2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */, 63ED3C082821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, 63ED3C562821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C242821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 63ED3C522821D230006A8A08 /* IndexingError.swift in Sources */, - 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 54B83CC51C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */, 63ED3C6A2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C5E2821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C6F2821D311006A8A08 /* ParsingError.swift in Sources */, @@ -818,7 +818,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63ED3C1D2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C1D2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */, 63ED3C112821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C152821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C672821D247006A8A08 /* FullXMLParser.swift in Sources */, @@ -829,12 +829,12 @@ 63ED3C2A2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C192821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CD7934C61A7581F500867857 /* XMLHash.swift in Sources */, - 63ED3C0D2821A723006A8A08 /* XMLDeserialization.swift in Sources */, + 63ED3C0D2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */, 63ED3C092821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, 63ED3C572821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C252821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 63ED3C532821D230006A8A08 /* IndexingError.swift in Sources */, - 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 54B83CC61C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */, 63ED3C6B2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C5F2821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C702821D311006A8A08 /* ParsingError.swift in Sources */, @@ -871,7 +871,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63ED3C1E2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C1E2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */, 63ED3C122821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C162821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C682821D247006A8A08 /* FullXMLParser.swift in Sources */, @@ -882,12 +882,12 @@ 63ED3C2B2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1A2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72731C00B0D900C10B28 /* XMLHash.swift in Sources */, - 63ED3C0E2821A723006A8A08 /* XMLDeserialization.swift in Sources */, + 63ED3C0E2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */, 63ED3C0A2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, 63ED3C582821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C262821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 63ED3C542821D230006A8A08 /* IndexingError.swift in Sources */, - 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 54B83CC71C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */, 63ED3C6C2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C602821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C712821D311006A8A08 /* ParsingError.swift in Sources */, @@ -924,7 +924,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63ED3C1F2821A723006A8A08 /* XMLIndexerDeserializable.swift in Sources */, + 63ED3C1F2821A723006A8A08 /* XMLObjectDeserialization.swift in Sources */, 63ED3C132821A723006A8A08 /* XMLAttributeDeserializable.swift in Sources */, 63ED3C172821A723006A8A08 /* Double+XMLDeserialization.swift in Sources */, 63ED3C692821D247006A8A08 /* FullXMLParser.swift in Sources */, @@ -935,12 +935,12 @@ 63ED3C2C2821AF4D006A8A08 /* XMLHashOptions.swift in Sources */, 63ED3C1B2821A723006A8A08 /* XMLElementDeserializable.swift in Sources */, CDEA72741C00B0E300C10B28 /* XMLHash.swift in Sources */, - 63ED3C0F2821A723006A8A08 /* XMLDeserialization.swift in Sources */, + 63ED3C0F2821A723006A8A08 /* XMLValueDeserialization.swift in Sources */, 63ED3C0B2821A723006A8A08 /* String+XMLDeserialization.swift in Sources */, 63ED3C592821D230006A8A08 /* XMLDeserializationError.swift in Sources */, 63ED3C272821A723006A8A08 /* Int+XMLDeserialization.swift in Sources */, 63ED3C552821D230006A8A08 /* IndexingError.swift in Sources */, - 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLIndexerDeserializable.swift in Sources */, + 54B83CC81C849D9B00D588B5 /* XMLIndexer+XMLDeserialization.swift in Sources */, 63ED3C6D2821D247006A8A08 /* Stack.swift in Sources */, 63ED3C612821D247006A8A08 /* IndexOp.swift in Sources */, 63ED3C722821D311006A8A08 /* ParsingError.swift in Sources */, diff --git a/SWXMLHashPlayground.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/SWXMLHashPlayground.playground/Pages/Introduction.xcplaygroundpage/Contents.swift index 77dfb616..73270e92 100644 --- a/SWXMLHashPlayground.playground/Pages/Introduction.xcplaygroundpage/Contents.swift +++ b/SWXMLHashPlayground.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -83,7 +83,7 @@ let booksXML = """ """ -struct Book: XMLIndexerDeserializable { +struct Book: XMLObjectDeserialization { let title: String let price: Double let year: Int diff --git a/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift b/Source/Deserialization/XMLIndexer+XMLDeserialization.swift similarity index 98% rename from Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift rename to Source/Deserialization/XMLIndexer+XMLDeserialization.swift index 4bdb1056..182e3203 100644 --- a/Source/Deserialization/XMLIndexer+XMLIndexerDeserializable.swift +++ b/Source/Deserialization/XMLIndexer+XMLDeserialization.swift @@ -1,5 +1,5 @@ // -// XMLIndexer.XMLIndexerDeserializable.swift +// XMLIndexer+XMLObjectDeserialization.swift // SWXMLHash // // Copyright (c) 2016 Maciek Grzybowskio @@ -274,7 +274,7 @@ public extension XMLIndexer { - returns: the deserialized `T` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> T { + func value() throws -> T { switch self { case .element: let deserialized = try T.deserialize(self) @@ -295,7 +295,7 @@ public extension XMLIndexer { - returns: the deserialized `T?` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> T? { + func value() throws -> T? { switch self { case .element: let deserialized = try T.deserialize(self) @@ -314,7 +314,7 @@ public extension XMLIndexer { - returns: the deserialized `[T]` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> [T] where T: XMLIndexerDeserializable { + func value() throws -> [T] where T: XMLObjectDeserialization { switch self { case .list(let elements): return try elements.map { @@ -343,7 +343,7 @@ public extension XMLIndexer { - returns: the deserialized `[T]?` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> [T]? { + func value() throws -> [T]? { switch self { case .list(let elements): return try elements.map { @@ -370,7 +370,7 @@ public extension XMLIndexer { - returns: the deserialized `[T?]` value - throws: an XMLDeserializationError is there is a problem with deserialization */ - func value() throws -> [T?] { + func value() throws -> [T?] { switch self { case .list(let elements): return try elements.map { diff --git a/Source/Deserialization/XMLIndexerDeserializable.swift b/Source/Deserialization/XMLObjectDeserialization.swift similarity index 96% rename from Source/Deserialization/XMLIndexerDeserializable.swift rename to Source/Deserialization/XMLObjectDeserialization.swift index ea59f434..4a659a65 100644 --- a/Source/Deserialization/XMLIndexerDeserializable.swift +++ b/Source/Deserialization/XMLObjectDeserialization.swift @@ -26,7 +26,7 @@ import Foundation /// Provides XMLIndexer deserialization / type transformation support -public protocol XMLIndexerDeserializable { +public protocol XMLObjectDeserialization { /// Method for deserializing elements from XMLIndexer static func deserialize(_ element: XMLIndexer) throws -> Self /// Method for validating elements post deserialization @@ -34,7 +34,7 @@ public protocol XMLIndexerDeserializable { } /// Provides XMLIndexer deserialization / type transformation support -public extension XMLIndexerDeserializable { +public extension XMLObjectDeserialization { /** A default implementation that will throw an error if it is called diff --git a/Source/Deserialization/XMLDeserialization.swift b/Source/Deserialization/XMLValueDeserialization.swift similarity index 95% rename from Source/Deserialization/XMLDeserialization.swift rename to Source/Deserialization/XMLValueDeserialization.swift index ed72fe82..8f6ecbcb 100644 --- a/Source/Deserialization/XMLDeserialization.swift +++ b/Source/Deserialization/XMLValueDeserialization.swift @@ -27,7 +27,7 @@ import Foundation /// This protocol implements both XMLAttributeDeserializable and XMLElementDeserializable meaning /// that it can be used to easily provide custom deserialization for your types. -public protocol XMLDeserialization: XMLAttributeDeserializable, XMLElementDeserializable { +public protocol XMLValueDeserialization: XMLAttributeDeserializable, XMLElementDeserializable { /// Method for deserializing elements from XMLElement static func deserialize(_ element: XMLElement) throws -> Self @@ -39,7 +39,7 @@ public protocol XMLDeserialization: XMLAttributeDeserializable, XMLElementDeseri } /// Provides XMLAttribute deserialization / type transformation support -public extension XMLDeserialization { +public extension XMLValueDeserialization { /** A default implementation that will throw an error if it is called diff --git a/Source/DeserializationTypes/Bool+XMLDeserialization.swift b/Source/DeserializationTypes/Bool+XMLDeserialization.swift index 6fd40ee3..518cab81 100644 --- a/Source/DeserializationTypes/Bool+XMLDeserialization.swift +++ b/Source/DeserializationTypes/Bool+XMLDeserialization.swift @@ -25,7 +25,7 @@ import Foundation -extension Bool: XMLDeserialization, XMLElementDeserializable { +extension Bool: XMLValueDeserialization { // swiftlint:disable line_length /** Attempts to deserialize XML element content to a Bool. This uses NSString's 'boolValue' diff --git a/Source/DeserializationTypes/Double+XMLDeserialization.swift b/Source/DeserializationTypes/Double+XMLDeserialization.swift index cf7aef32..94a2e104 100644 --- a/Source/DeserializationTypes/Double+XMLDeserialization.swift +++ b/Source/DeserializationTypes/Double+XMLDeserialization.swift @@ -25,7 +25,7 @@ import Foundation -extension Double: XMLDeserialization, XMLElementDeserializable { +extension Double: XMLValueDeserialization { /** Attempts to deserialize XML element content to a Double diff --git a/Source/DeserializationTypes/Float+XMLDeserialization.swift b/Source/DeserializationTypes/Float+XMLDeserialization.swift index 489982a1..f7c7e3aa 100644 --- a/Source/DeserializationTypes/Float+XMLDeserialization.swift +++ b/Source/DeserializationTypes/Float+XMLDeserialization.swift @@ -25,7 +25,7 @@ import Foundation -extension Float: XMLDeserialization { +extension Float: XMLValueDeserialization { /** Attempts to deserialize XML element content to a Float diff --git a/Source/DeserializationTypes/Int+XMLDeserialization.swift b/Source/DeserializationTypes/Int+XMLDeserialization.swift index 44643e70..ce07c266 100644 --- a/Source/DeserializationTypes/Int+XMLDeserialization.swift +++ b/Source/DeserializationTypes/Int+XMLDeserialization.swift @@ -25,7 +25,7 @@ import Foundation -extension Int: XMLDeserialization { +extension Int: XMLValueDeserialization { /** Attempts to deserialize XML element content to a Int diff --git a/Source/DeserializationTypes/String+XMLDeserialization.swift b/Source/DeserializationTypes/String+XMLDeserialization.swift index 59129f68..b1ac346a 100644 --- a/Source/DeserializationTypes/String+XMLDeserialization.swift +++ b/Source/DeserializationTypes/String+XMLDeserialization.swift @@ -25,7 +25,7 @@ import Foundation -extension String: XMLDeserialization { +extension String: XMLValueDeserialization { /** Attempts to deserialize XML element content to a String diff --git a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift index f67c7759..47daa1e7 100644 --- a/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionBasicTypesTests.swift @@ -637,7 +637,7 @@ class TypeConversionBasicTypesTests: XCTestCase { enum BasicItemValidation: Error { case priceOutOfBounds(Double) } -struct BasicItem: XMLIndexerDeserializable { +struct BasicItem: XMLObjectDeserialization { let name: String let price: Double let id: String diff --git a/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift index 0739e10e..905453c2 100644 --- a/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift +++ b/Tests/SWXMLHashTests/TypeConversionComplexTypesTests.swift @@ -134,7 +134,7 @@ class TypeConversionComplexTypesTests: XCTestCase { } } -struct ComplexItem: XMLIndexerDeserializable { +struct ComplexItem: XMLObjectDeserialization { let name: String let priceOptional: Double? let basics: [BasicItem] From 1a8fcbceb62dda292e7d58929d442bb2a4d56aad Mon Sep 17 00:00:00 2001 From: David Mohundro Date: Tue, 3 May 2022 20:15:26 -0500 Subject: [PATCH 15/16] docs: minor tweaks related to update --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6307f941..84c45a91 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ With the following implementation for `Date` element and attribute deserialization: ```swift -extension Date: XMLDeserialization { +extension Date: XMLValueDeserialization { public static func deserialize(_ element: XMLElement) throws -> Date { let date = stringToDate(element.text) @@ -480,10 +480,10 @@ Given: ``` -with `Book` struct implementing `XMLIndexerDeserializable`: +with `Book` struct implementing `XMLObjectDeserialization`: ```swift -struct Book: XMLIndexerDeserializable { +struct Book: XMLObjectDeserialization { let title: String let price: Double let year: Int @@ -513,7 +513,7 @@ let books: [Book] = try xml["root"]["books"]["book"].value() Types Conversion You can convert any XML to your custom type by implementing -`XMLIndexerDeserializable` for any non-leaf node (e.g. `` in the example +`XMLObjectDeserialization` for any non-leaf node (e.g. `` in the example above). For leaf nodes (e.g. `` in the example above), built-in converters @@ -546,7 +546,7 @@ deserialization, etc.). ### I'm getting an "Ambiguous reference to member 'subscript'" when I call `.value()`. `.value()` is used for deserialization - you have to have something that -implements `XMLIndexerDeserializable` (or `XMLElementDeserializable` if it is a +implements `XMLObjectDeserialization` (or `XMLElementDeserializable` if it is a single element versus a group of elements) and that can handle deserialization to the left-hand side of expression. From 54e2186ee4c33d69936bd393b929dda68f384376 Mon Sep 17 00:00:00 2001 From: David Mohundro <david@mohundro.com> Date: Fri, 6 May 2022 19:47:42 -0500 Subject: [PATCH 16/16] docs: update docs for release --- CHANGELOG.md | 7 +++ README.md | 130 +++++++++++++++++++++++++++------------------------ 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8c36ed..f7c53a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v7.0.0 (May 6, 2022) + +- Added `XMLValueDeserialization` which implements both `XMLElementDeserializable` and `XMLAttributeDeserializable`. It makes it a lot easier and simpler to implement custom deserialization for types (e.g. Dates) +- Renamed `XMLIndexerDeserializable` to `XMLObjectDeserialization`. This is a more useful name and better indicates its purpose as compared to the `XMLValueDeserialization`. +- Changed all errors types so that they implement `LocalizedError` (previously, they implemented `CustomStringConvertible`). This should help a lot with handling errors. +- Internally, all files have been heavily reorganized - types are now in their own files. + ## v6.0.0 (September 28, 2021) - Rename main class from `SWXMLHash` to `XMLHash` so that the module name doesn't conflict with the class name. There are a decent number of issues related to naming conflicts so hopefully this will avoid them. Fixes [#242](https://github.com/drmohundro/SWXMLHash/issues/242). diff --git a/README.md b/README.md index 84c45a91..a635a020 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ platform :ios, '10.0' use_frameworks! target 'YOUR_TARGET_NAME' do - pod 'SWXMLHash', '~> 6.0.0' + pod 'SWXMLHash', '~> 7.0.0' end ``` @@ -73,7 +73,7 @@ $ brew install carthage Then add the following line to your `Cartfile`: ``` -github "drmohundro/SWXMLHash" ~> 6.0 +github "drmohundro/SWXMLHash" ~> 7.0 ``` ### Swift Package Manager @@ -83,7 +83,7 @@ Swift Package Manager requires Swift version 4.0 or higher. First, create a ```swift dependencies: [ - .package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "6.0.0") + .package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "7.0.0") ] ``` @@ -94,12 +94,10 @@ dependencies: [ To install manually, you'll need to clone the SWXMLHash repository. You can do this in a separate directory, or you can make use of git submodules - in this case, git submodules are recommended so that your repository has details about -which commit of SWXMLHash you're using. Once this is done, you can just drop the -`SWXMLHash.swift` file into your project. +which commit of SWXMLHash you're using. Once this is done, you can just drop all +of the relevant swift files into your project. -> NOTE: if you're targeting iOS 7, you'll have to install manually because -> embedded frameworks require a minimum deployment target of iOS 8 or OSX -> Mavericks. +If you're using a workspace, though, you can just include the entire `SWXMLHash.xcodeproj`. ## Getting Started @@ -388,60 +386,11 @@ lazy parsing doesn't actually occur until the `element` or `all` method are called - as a result, there isn't any way to know prior to asking for an element if it exists or not. -### Simple Type Conversion +### XML Deserialization Into Objects -Given: - -```xml -<root> - <elem>Monday, 23 January 2016 12:01:12 111</elem> -</root> -``` - -With the following implementation for `Date` element and attribute -deserialization: - -```swift -extension Date: XMLValueDeserialization { - public static func deserialize(_ element: XMLElement) throws -> Date { - let date = stringToDate(element.text) - - guard let validDate = date else { - throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element) - } - - return validDate - } - - public static func deserialize(_ attribute: XMLAttribute) throws -> Date { - let date = stringToDate(attribute.text) - - guard let validDate = date else { - throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute) - } - - return validDate - } - - public func validate() throws { - // empty validate... only necessary for custom validation logic after parsing - } - - private static func stringToDate(_ dateAsString: String) -> Date? { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "EEEE, dd MMMM yyyy HH:mm:ss SSS" - return dateFormatter.date(from: dateAsString) - } -} -``` - -The below will return a date value: - -```swift -let dt: Date = try xml["root"]["elem"].value() -``` - -### Complex Types Conversion +Even more often, you'll want to deserialize an XML tree into an +array of custom types. This is where `XMLObjectDeserialization` +comes into play. Given: @@ -529,6 +478,63 @@ Types conversion supports error handling, optionals and arrays. For more examples, look into `SWXMLHashTests.swift` or play with types conversion directly in the Swift playground. +### Custom Value Conversion + +Value deserialization is where a specific string value needs to be deserialized +into a custom type. So, date is a good example here - you'd rather deal with +date types than doing string parsing, right? That's what the `XMLValueDeserialization` +attribute is for. + +Given: + +```xml +<root> + <elem>Monday, 23 January 2016 12:01:12 111</elem> +</root> +``` + +With the following implementation for `Date` value deserialization: + +```swift +extension Date: XMLValueDeserialization { + public static func deserialize(_ element: XMLElement) throws -> Date { + let date = stringToDate(element.text) + + guard let validDate = date else { + throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element) + } + + return validDate + } + + public static func deserialize(_ attribute: XMLAttribute) throws -> Date { + let date = stringToDate(attribute.text) + + guard let validDate = date else { + throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute) + } + + return validDate + } + + public func validate() throws { + // empty validate... only necessary for custom validation logic after parsing + } + + private static func stringToDate(_ dateAsString: String) -> Date? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "EEEE, dd MMMM yyyy HH:mm:ss SSS" + return dateFormatter.date(from: dateAsString) + } +} +``` + +The below will return a date value: + +```swift +let dt: Date = try xml["root"]["elem"].value() +``` + ## FAQ ### Does SWXMLHash handle URLs for me? @@ -612,7 +618,7 @@ extension NSDate: XMLElementDeserializable { ### How do I handle deserialization with an enum? -Check out this great suggestion/example from @woolie up at https://github.com/drmohundro/SWXMLHash/discussions/245. +Check out this great suggestion/example from @woolie up at <https://github.com/drmohundro/SWXMLHash/discussions/245>. ### Have a different question?