Skip to content

Commit 1dec74a

Browse files
authored
Merge pull request #10 from LiveUI/linuxEncodingTest
Moved the encoder to native Swift Types from NS* to be more typesafe
2 parents 16c4009 + fb395e9 commit 1dec74a

6 files changed

Lines changed: 342 additions & 274 deletions

File tree

Jenkinsfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ pipeline {
2424
}
2525
}
2626
}
27+

Sources/XMLCoding/Encoder/XMLEncoder.swift

Lines changed: 162 additions & 196 deletions
Large diffs are not rendered by default.

Sources/XMLCoding/Encoder/XMLEncodingStorage.swift

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,111 @@
99

1010
import Foundation
1111

12+
// MARK: - Mutable Containers
13+
14+
internal class MutableContainerDictionary {
15+
private var values: [String: MutableContainer] = [:]
16+
17+
subscript(key: String) -> MutableContainer? {
18+
get {
19+
return values[key]
20+
}
21+
set(newValue) {
22+
values[key] = newValue
23+
}
24+
}
25+
26+
internal func asContainer() -> Container {
27+
let transformedValues: [String: Container] = values.mapValues { value in
28+
return value.asContainer()
29+
}
30+
31+
return .dictionary(transformedValues)
32+
}
33+
}
34+
35+
internal class MutableContainerArray {
36+
private var values: [MutableContainer] = []
37+
38+
subscript(index: Int) -> MutableContainer {
39+
get {
40+
return values[index]
41+
}
42+
}
43+
44+
var count: Int {
45+
return values.count
46+
}
47+
48+
func append(_ newElement: MutableContainer) {
49+
values.append(newElement)
50+
}
51+
52+
func insert(_ newElement: MutableContainer, at index: Int) {
53+
values.insert(newElement, at: index)
54+
}
55+
56+
internal func asContainer() -> Container {
57+
let transformedValues: [Container] = values.map { value in
58+
return value.asContainer()
59+
}
60+
61+
return .array(transformedValues)
62+
}
63+
}
64+
65+
internal enum MutableContainer {
66+
case dictionary(MutableContainerDictionary)
67+
case array(MutableContainerArray)
68+
case boolean(Bool)
69+
case string(String)
70+
case int64(Int64)
71+
case uint64(UInt64)
72+
case double(Double)
73+
case null
74+
75+
internal func asContainer() -> Container {
76+
switch self {
77+
case .dictionary(let innerDictionary):
78+
return innerDictionary.asContainer()
79+
case .array(let innerArray):
80+
return innerArray.asContainer()
81+
case .boolean(let value):
82+
return .boolean(value)
83+
case .string(let value):
84+
return .string(value)
85+
case .int64(let value):
86+
return .int64(value)
87+
case .uint64(let value):
88+
return .uint64(value)
89+
case .double(let value):
90+
return .double(value)
91+
case .null:
92+
return .null
93+
}
94+
}
95+
}
96+
97+
// MARK: - Container
98+
99+
internal enum Container {
100+
case dictionary([String: Container])
101+
case array([Container])
102+
case boolean(Bool)
103+
case string(String)
104+
case int64(Int64)
105+
case uint64(UInt64)
106+
case double(Double)
107+
case null
108+
}
109+
12110
// MARK: - Encoding Storage and Containers
13111

14112
internal struct _XMLEncodingStorage {
15113
// MARK: Properties
16114

17115
/// The container stack.
18-
/// Elements may be any one of the XML types (NSNull, NSNumber, NSString, NSArray, NSDictionary).
19-
private(set) internal var containers: [NSObject] = []
116+
private(set) internal var containers: [MutableContainer] = []
20117

21118
// MARK: - Initialization
22119

@@ -29,23 +126,27 @@ internal struct _XMLEncodingStorage {
29126
return self.containers.count
30127
}
31128

32-
internal mutating func pushKeyedContainer() -> NSMutableDictionary {
33-
let dictionary = NSMutableDictionary()
34-
self.containers.append(dictionary)
129+
internal mutating func pushKeyedContainer() -> MutableContainerDictionary {
130+
let dictionary = MutableContainerDictionary()
131+
self.containers.append(.dictionary(dictionary))
35132
return dictionary
36133
}
37134

38-
internal mutating func pushUnkeyedContainer() -> NSMutableArray {
39-
let array = NSMutableArray()
40-
self.containers.append(array)
135+
internal mutating func pushUnkeyedContainer() -> MutableContainerArray {
136+
let array = MutableContainerArray()
137+
self.containers.append(.array(array))
41138
return array
42139
}
43140

44-
internal mutating func push(container: NSObject) {
141+
internal mutating func push(container: MutableContainer) {
45142
self.containers.append(container)
46143
}
47144

48-
internal mutating func popContainer() -> NSObject {
145+
internal mutating func pushNull() {
146+
self.containers.append(.null)
147+
}
148+
149+
internal mutating func popContainer() -> MutableContainer {
49150
precondition(self.containers.count > 0, "Empty container stack.")
50151
return self.containers.popLast()!
51152
}

Sources/XMLCoding/Encoder/XMLReferencingEncoder.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ internal class _XMLReferencingEncoder : _XMLEncoder {
1818
/// The type of container we're referencing.
1919
private enum Reference {
2020
/// Referencing a specific index in an array container.
21-
case array(NSMutableArray, Int)
21+
case array(MutableContainerArray, Int)
2222

2323
/// Referencing a specific key in a dictionary container.
24-
case dictionary(NSMutableDictionary, String)
24+
case dictionary(MutableContainerDictionary, String)
2525
}
2626

2727
// MARK: - Properties
@@ -35,7 +35,7 @@ internal class _XMLReferencingEncoder : _XMLEncoder {
3535
// MARK: - Initialization
3636

3737
/// Initializes `self` by referencing the given array container in the given encoder.
38-
internal init(referencing encoder: _XMLEncoder, at index: Int, wrapping array: NSMutableArray) {
38+
internal init(referencing encoder: _XMLEncoder, at index: Int, wrapping array: MutableContainerArray) {
3939
self.encoder = encoder
4040
self.reference = .array(array, index)
4141
super.init(options: encoder.options, codingPath: encoder.codingPath)
@@ -45,7 +45,7 @@ internal class _XMLReferencingEncoder : _XMLEncoder {
4545

4646
/// Initializes `self` by referencing the given dictionary container in the given encoder.
4747
internal init(referencing encoder: _XMLEncoder,
48-
key: CodingKey, convertedKey: CodingKey, wrapping dictionary: NSMutableDictionary) {
48+
key: CodingKey, convertedKey: CodingKey, wrapping dictionary: MutableContainerDictionary) {
4949
self.encoder = encoder
5050
self.reference = .dictionary(dictionary, convertedKey.stringValue)
5151
super.init(options: encoder.options, codingPath: encoder.codingPath)
@@ -66,9 +66,9 @@ internal class _XMLReferencingEncoder : _XMLEncoder {
6666

6767
// Finalizes `self` by writing the contents of our storage to the referenced encoder's storage.
6868
deinit {
69-
let value: Any
69+
let value: MutableContainer
7070
switch self.storage.count {
71-
case 0: value = NSDictionary()
71+
case 0: value = .dictionary(.init())
7272
case 1: value = self.storage.popContainer()
7373
default: fatalError("Referencing encoder deallocated with multiple containers on stack.")
7474
}
@@ -78,7 +78,7 @@ internal class _XMLReferencingEncoder : _XMLEncoder {
7878
array.insert(value, at: index)
7979

8080
case .dictionary(let dictionary, let key):
81-
dictionary[NSString(string: key)] = value
81+
dictionary[key] = value
8282
}
8383
}
8484
}

Sources/XMLCoding/XMLStackParser.swift

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -79,80 +79,80 @@ internal class _XMLElement {
7979
self.init(key: key, value: nil, attributes: attributes.mapValues({ $0.description }), children: children)
8080
}
8181

82-
static func createRootElement(rootKey: String, object: NSObject) -> _XMLElement? {
82+
static func createRootElement(rootKey: String, header: XMLHeader?, options: XMLEncoder._Options, object: Container) throws -> Data {
8383
let element = _XMLElement(key: rootKey)
8484

85-
if let object = object as? NSDictionary {
86-
_XMLElement.modifyElement(element: element, parentElement: nil, key: nil, object: object)
87-
} else if let object = object as? NSArray {
88-
_XMLElement.createElement(parentElement: element, key: rootKey, object: object)
85+
switch object {
86+
case .dictionary(let dictionary):
87+
_XMLElement.modifyElement(element: element, parentElement: nil, key: nil, values: dictionary)
88+
case .array(let array):
89+
_XMLElement.createElement(parentElement: element, key: rootKey, object: array)
90+
default:
91+
throw EncodingError.invalidValue(object, EncodingError.Context(codingPath: [], debugDescription: "Top-level encoded as non-root XML fragment."))
8992
}
9093

91-
return element
94+
return element.toXMLString(with: header, withCDATA: options.stringEncodingStrategy != .deferredToString).data(using: .utf8, allowLossyConversion: true)!
9295
}
9396

94-
fileprivate static func createElement(parentElement: _XMLElement?, key: String, object: NSDictionary) {
97+
fileprivate static func createElement(parentElement: _XMLElement?, key: String, object: [String: Container]) {
9598
let element = _XMLElement(key: key)
9699

97-
modifyElement(element: element, parentElement: parentElement, key: key, object: object)
100+
modifyElement(element: element, parentElement: parentElement, key: key, values: object)
98101
}
99102

100-
fileprivate static func modifyElement(element: _XMLElement, parentElement: _XMLElement?, key: String?, object: NSDictionary) {
101-
element.attributes = (object[_XMLElement.attributesKey] as? [String: Any])?.mapValues({ String(describing: $0) }) ?? [:]
103+
private static func modifyContainerElement(container: Container, element: _XMLElement, key: String) {
104+
switch container {
105+
case .dictionary(let values):
106+
_XMLElement.createElement(parentElement: element, key: key, object: values)
107+
case .array(let values):
108+
_XMLElement.createElement(parentElement: element, key: key, object: values)
109+
case .boolean(let value):
110+
_XMLElement.createElement(parentElement: element, key: key, value: value)
111+
case .string(let value):
112+
_XMLElement.createElement(parentElement: element, key: key, value: value)
113+
case .int64(let value):
114+
_XMLElement.createElement(parentElement: element, key: key, value: value)
115+
case .uint64(let value):
116+
_XMLElement.createElement(parentElement: element, key: key, value: value)
117+
case .double(let value):
118+
_XMLElement.createElement(parentElement: element, key: key, value: value)
119+
case .null:
120+
_XMLElement.createNullElement(parentElement: element, key: key)
121+
}
122+
}
123+
124+
private static func modifyElement(element: _XMLElement, parentElement: _XMLElement?, key: String?, values: [String: Container]) {
125+
if let attributesContainer = values[_XMLElement.attributesKey], case let .dictionary(attributes) = attributesContainer {
126+
element.attributes = attributes.mapValues({ String(describing: $0) })
127+
}
102128

103-
let objects: [(String, NSObject)] = object.compactMap({
104-
guard let key = $0 as? String, let value = $1 as? NSObject, key != _XMLElement.attributesKey else { return nil }
129+
let filteredValues: [(String, Container)] = values.compactMap({
130+
guard $0 != _XMLElement.attributesKey else { return nil }
105131

106-
return (key, value)
132+
return ($0, $1)
107133
})
108134

109-
for (key, value) in objects {
110-
if let dict = value as? NSDictionary {
111-
_XMLElement.createElement(parentElement: element, key: key, object: dict)
112-
} else if let array = value as? NSArray {
113-
_XMLElement.createElement(parentElement: element, key: key, object: array)
114-
} else if let string = value as? NSString {
115-
_XMLElement.createElement(parentElement: element, key: key, object: string)
116-
} else if let number = value as? NSNumber {
117-
_XMLElement.createElement(parentElement: element, key: key, object: number)
118-
} else {
119-
_XMLElement.createElement(parentElement: element, key: key, object: NSNull())
120-
}
135+
for (key, value) in filteredValues {
136+
modifyContainerElement(container: value, element: element, key: key)
121137
}
122138

123139
if let parentElement = parentElement, let key = key {
124140
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
125141
}
126142
}
127143

128-
fileprivate static func createElement(parentElement: _XMLElement, key: String, object: NSArray) {
129-
let objects = object.compactMap({ $0 as? NSObject })
130-
objects.forEach({
131-
if let dict = $0 as? NSDictionary {
132-
_XMLElement.createElement(parentElement: parentElement, key: key, object: dict)
133-
} else if let array = $0 as? NSArray {
134-
_XMLElement.createElement(parentElement: parentElement, key: key, object: array)
135-
} else if let string = $0 as? NSString {
136-
_XMLElement.createElement(parentElement: parentElement, key: key, object: string)
137-
} else if let number = $0 as? NSNumber {
138-
_XMLElement.createElement(parentElement: parentElement, key: key, object: number)
139-
} else {
140-
_XMLElement.createElement(parentElement: parentElement, key: key, object: NSNull())
141-
}
144+
fileprivate static func createElement(parentElement: _XMLElement, key: String, object: [Container]) {
145+
object.forEach({
146+
modifyContainerElement(container: $0, element: parentElement, key: key)
142147
})
143148
}
144-
145-
fileprivate static func createElement(parentElement: _XMLElement, key: String, object: NSNumber) {
146-
let element = _XMLElement(key: key, value: object.description)
147-
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
148-
}
149-
150-
fileprivate static func createElement(parentElement: _XMLElement, key: String, object: NSString) {
151-
let element = _XMLElement(key: key, value: object.description)
149+
150+
fileprivate static func createElement(parentElement: _XMLElement, key: String, value: CustomStringConvertible) {
151+
let element = _XMLElement(key: key, value: value.description)
152152
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
153153
}
154154

155-
fileprivate static func createElement(parentElement: _XMLElement, key: String, object: NSNull) {
155+
fileprivate static func createNullElement(parentElement: _XMLElement, key: String) {
156156
let element = _XMLElement(key: key)
157157
parentElement.children[key] = (parentElement.children[key] ?? []) + [element]
158158
}

0 commit comments

Comments
 (0)