Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 43 additions & 24 deletions Sources/XMLCoder/Auxiliaries/XMLElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ struct _XMLElement {

elements = Dictionary(uniqueKeysWithValues: box.elements.map { key, box in
switch box {
case let sharedUnkeyedBox as SharedBox<UnkeyedBox>:
let box = sharedUnkeyedBox.unbox() as! UnkeyedBox
let elements = box.map { _XMLElement(key: key, box: $0) }
return (key, elements)
case let unkeyedBox as UnkeyedBox:
// This basically injects the unkeyed children directly into self:
let elements = unkeyedBox.map { _XMLElement(key: key, box: $0) }
return (key, elements)
case let sharedKeyedBox as SharedBox<KeyedBox>:
let box = sharedKeyedBox.unbox() as! KeyedBox
let elements = [_XMLElement(key: key, box: box)]
return (key, elements)
case let keyedBox as KeyedBox:
let elements = [_XMLElement(key: key, box: keyedBox)]
return (key, elements)
Expand All @@ -66,6 +74,10 @@ struct _XMLElement {

init(key: String, box: Box) {
switch box {
case let sharedUnkeyedBox as SharedBox<UnkeyedBox>:
self.init(key: key, box: sharedUnkeyedBox.unbox())
case let sharedKeyedBox as SharedBox<KeyedBox>:
self.init(key: key, box: sharedKeyedBox.unbox())
case let unkeyedBox as UnkeyedBox:
self.init(key: key, box: unkeyedBox)
case let keyedBox as KeyedBox:
Expand Down Expand Up @@ -93,44 +105,51 @@ struct _XMLElement {
var elements: [String: Box] = [:]
for (key, value) in self.elements {
for child in value {
if let content = child.value {
switch elements[key] {
case let unkeyedBox as UnkeyedBox:
unkeyedBox.append(StringBox(content))
elements[key] = unkeyedBox
case let keyedBox as StringBox:
elements[key] = UnkeyedBox([keyedBox, StringBox(content)])
default:
elements[key] = StringBox(content)
let hasValue = child.value != nil
let hasElements = !child.elements.isEmpty
let hasAttributes = !child.attributes.isEmpty

if hasValue || hasElements || hasAttributes {
if let content = child.value {
switch elements[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(StringBox(content))
elements[key] = unkeyedBox
case let stringBox as StringBox:
elements[key] = UnkeyedBox([stringBox, StringBox(content)])
default:
elements[key] = StringBox(content)
}
}
} else if !child.elements.isEmpty || !child.attributes.isEmpty {
let content = child.flatten()
if let existingValue = elements[key] {
if let unkeyedBox = existingValue as? UnkeyedBox {
var boxes = unkeyedBox.unbox()
boxes.append(content)
elements[key] = UnkeyedBox(boxes)
} else {
elements[key] = UnkeyedBox([existingValue, content])
if hasElements || hasAttributes {
let content = child.flatten()
switch elements[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(content)
elements[key] = unkeyedBox
case let box?:
elements[key] = UnkeyedBox([box, content])
default:
elements[key] = content
}
} else {
elements[key] = content
}
} else {
switch elements[key] {
case let unkeyedBox as UnkeyedBox:
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(NullBox())
elements[key] = unkeyedBox
case let keyedBox as StringBox:
elements[key] = UnkeyedBox([keyedBox, NullBox()])
case let box?:
elements[key] = UnkeyedBox([box, NullBox()])
default:
elements[key] = NullBox()
}
}
}
}

return KeyedBox(elements: elements, attributes: attributes)
let keyedBox = KeyedBox(elements: elements, attributes: attributes)

return keyedBox
}

func toXMLString(with header: XMLHeader? = nil, withCDATA cdata: Bool, formatting: XMLEncoder.OutputFormatting, ignoreEscaping _: Bool = false) -> String {
Expand Down
8 changes: 7 additions & 1 deletion Sources/XMLCoder/Box/Box.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ protocol Box {
}

/// A box that only describes a single atomic value.
protocol SimpleBox: Box {}
protocol SimpleBox: Box {
// A simple tagging protocol, for now.
}

protocol SharedBoxProtocol {
func unbox() -> Box
}
2 changes: 1 addition & 1 deletion Sources/XMLCoder/Box/KeyedBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ extension KeyedStorage: CustomStringConvertible {
}
}

class KeyedBox {
struct KeyedBox {
typealias Key = String
typealias Attribute = SimpleBox
typealias Element = Box
Expand Down
36 changes: 36 additions & 0 deletions Sources/XMLCoder/Box/SharedBox.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// SharedBox.swift
// XMLCoder
//
// Created by Vincent Esche on 12/22/18.
//

import Foundation

class SharedBox<Unboxed: Box> {
fileprivate var unboxed: Unboxed

init(_ wrapped: Unboxed) {
unboxed = wrapped
}

func withShared<Result>(_ body: (inout Unboxed) throws -> Result) rethrows -> Result {
return try body(&unboxed)
}
}

extension SharedBox: Box {
var isNull: Bool {
return unboxed.isNull
}

func xmlString() -> String? {
return unboxed.xmlString()
}
}

extension SharedBox: SharedBoxProtocol {
func unbox() -> Box {
return unboxed
}
}
6 changes: 3 additions & 3 deletions Sources/XMLCoder/Box/UnkeyedBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

// Minimalist implementation of an order-preserving unkeyed box:
class UnkeyedBox {
struct UnkeyedBox {
typealias Element = Box
typealias Unboxed = [Element]

Expand All @@ -35,11 +35,11 @@ class UnkeyedBox {
return unboxed
}

func append(_ newElement: Element) {
mutating func append(_ newElement: Element) {
unboxed.append(newElement)
}

func insert(_ newElement: Element, at index: Int) {
mutating func insert(_ newElement: Element, at index: Int) {
unboxed.insert(newElement, at: index)
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/XMLCoder/Decoder/XMLDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ class _XMLDecoder: Decoder {
))
}

guard let keyed = topContainer as? KeyedBox else {
guard let keyed = topContainer as? SharedBox<KeyedBox> else {
throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: topContainer)
}

Expand All @@ -350,7 +350,7 @@ class _XMLDecoder: Decoder {
))
}

let unkeyed = (topContainer as? UnkeyedBox) ?? UnkeyedBox([topContainer])
let unkeyed = (topContainer as? SharedBox<UnkeyedBox>) ?? SharedBox(UnkeyedBox([topContainer]))

return _XMLUnkeyedDecodingContainer(referencing: self, wrapping: unkeyed)
}
Expand Down
8 changes: 7 additions & 1 deletion Sources/XMLCoder/Decoder/XMLDecodingStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ struct _XMLDecodingStorage {
}

mutating func push(container: Box) {
containers.append(container)
if let keyedBox = container as? KeyedBox {
containers.append(SharedBox(keyedBox))
} else if let unkeyedBox = container as? UnkeyedBox {
containers.append(SharedBox(unkeyedBox))
} else {
containers.append(container)
}
}

@discardableResult
Expand Down
Loading