Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make value intrinsic smarter #89

Merged
merged 8 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Shawn Moore
Copyright (c) 2018-2019 Shawn Moore and XMLCoder contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
81 changes: 2 additions & 79 deletions Sources/XMLCoder/Auxiliaries/Box/KeyedBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,6 @@
// Created by Vincent Esche on 11/19/18.
//

struct KeyedStorage<Key: Hashable & Comparable, Value> {
struct Iterator: IteratorProtocol {
fileprivate var orderIterator: Order.Iterator
fileprivate var buffer: Buffer
mutating func next() -> (Key, Value)? {
guard
let key = orderIterator.next(),
let value = buffer[key]
else { return nil }

return (key, value)
}
}

typealias Buffer = [Key: Value]
typealias Order = [Key]

fileprivate var order = Order()
fileprivate var buffer = Buffer()

var isEmpty: Bool {
return buffer.isEmpty
}

var count: Int {
return buffer.count
}

var keys: Buffer.Keys {
return buffer.keys
}

init<S>(_ sequence: S) where S: Sequence, S.Element == (Key, Value) {
order = sequence.map { $0.0 }
buffer = Dictionary(uniqueKeysWithValues: sequence)
}

subscript(key: Key) -> Value? {
get {
return buffer[key]
}
set {
if buffer[key] == nil {
order.append(key)
}
buffer[key] = newValue
}
}

func map<T>(_ transform: (Key, Value) throws -> T) rethrows -> [T] {
return try buffer.map(transform)
}

func compactMap<T>(_ transform: ((Key, Value)) throws -> T?) rethrows -> [T] {
return try buffer.compactMap(transform)
}

init() {}
}

extension KeyedStorage: Sequence {
func makeIterator() -> Iterator {
return Iterator(orderIterator: order.makeIterator(), buffer: buffer)
}
}

extension KeyedStorage: CustomStringConvertible {
var description: String {
let result = order.compactMap { (key: Key) -> String? in
guard let value = buffer[key] else { return nil }

return "\"\(key)\": \(value)"
}.joined(separator: ", ")

return "[\(result)]"
}
}

struct KeyedBox {
typealias Key = String
typealias Attribute = SimpleBox
Expand All @@ -104,7 +26,8 @@ struct KeyedBox {

extension KeyedBox {
init<E, A>(elements: E, attributes: A)
where E: Sequence, E.Element == (Key, Element), A: Sequence, A.Element == (Key, Attribute) {
where E: Sequence, E.Element == (Key, Element),
A: Sequence, A.Element == (Key, Attribute) {
let elements = Elements(elements)
let attributes = Attributes(attributes)
self.init(elements: elements, attributes: attributes)
Expand Down
149 changes: 149 additions & 0 deletions Sources/XMLCoder/Auxiliaries/KeyedStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//
// KeyedStorage.swift
// XMLCoder
//
// Created by Max Desiatov on 07/04/2019.
//

struct KeyedStorage<Key: Hashable & Comparable, Value> {
struct Iterator: IteratorProtocol {
fileprivate var orderIterator: Order.Iterator
fileprivate var buffer: Buffer
mutating func next() -> (Key, Value)? {
guard
let key = orderIterator.next(),
let value = buffer[key]
else { return nil }

return (key, value)
}
}

typealias Buffer = [Key: Value]
typealias Order = [Key]

fileprivate var order = Order()
fileprivate var buffer = Buffer()

var isEmpty: Bool {
return buffer.isEmpty
}

var count: Int {
return buffer.count
}

var keys: Buffer.Keys {
return buffer.keys
}

init<S>(_ sequence: S) where S: Sequence, S.Element == (Key, Value) {
order = sequence.map { $0.0 }
buffer = Dictionary(uniqueKeysWithValues: sequence)
}

subscript(key: Key) -> Value? {
get {
return buffer[key]
}
set {
if buffer[key] == nil {
order.append(key)
}
buffer[key] = newValue
}
}

func map<T>(_ transform: (Key, Value) throws -> T) rethrows -> [T] {
return try buffer.map(transform)
}

func compactMap<T>(
_ transform: ((Key, Value)) throws -> T?
) rethrows -> [T] {
return try buffer.compactMap(transform)
}

init() {}
}

extension KeyedStorage: Sequence {
func makeIterator() -> Iterator {
return Iterator(orderIterator: order.makeIterator(), buffer: buffer)
}
}

extension KeyedStorage: CustomStringConvertible {
var description: String {
let result = order.compactMap { (key: Key) -> String? in
guard let value = buffer[key] else { return nil }

return "\"\(key)\": \(value)"
}.joined(separator: ", ")

return "[\(result)]"
}
}

private extension KeyedStorage where Key == String, Value == Box {
mutating func merge(value: String, at key: String) {
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(StringBox(value))
self[key] = unkeyedBox
case let stringBox as StringBox:
self[key] = UnkeyedBox([stringBox, StringBox(value)])
default:
self[key] = StringBox(value)
}
}

mutating func mergeElementsAttributes(from element: XMLCoderElement) {
let hasValue = element.value != nil

let key = element.key
let content = element.flatten()
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(content)
self[key] = unkeyedBox
case let keyedBox as KeyedBox:
self[key] = UnkeyedBox([keyedBox, content])
case let box? where !hasValue:
self[key] = UnkeyedBox([box, content])
default:
self[key] = content
}
}

mutating func mergeNull(at key: String) {
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(NullBox())
self[key] = unkeyedBox
case let box?:
self[key] = UnkeyedBox([box, NullBox()])
default:
self[key] = NullBox()
}
}
}

extension KeyedStorage where Key == String, Value == Box {
func merge(element: XMLCoderElement) -> KeyedStorage<String, Box> {
var result = self

let hasElements = !element.elements.isEmpty
let hasAttributes = !element.attributes.isEmpty

if hasElements || hasAttributes {
result.mergeElementsAttributes(from: element)
} else if let value = element.value {
result.merge(value: value, at: element.key)
} else {
result.mergeNull(at: element.key)
}

return result
}
}
56 changes: 4 additions & 52 deletions Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,59 +44,13 @@ struct XMLCoderElement: Equatable {
elements.append(element)
}

// FIXME: this should be split into separate functions and
// thoroughtly tested
func flatten() -> KeyedBox {
let attributes = KeyedStorage(self.attributes.mapValues {
StringBox($0) as SimpleBox
}.shuffled())
let storage = KeyedStorage<String, Box>()

var elements = self.elements.reduce(storage) { result, element in
var result = result
let key = element.key

let hasValue = element.value != nil
let hasElements = !element.elements.isEmpty
let hasAttributes = !element.attributes.isEmpty

if hasValue || hasElements || hasAttributes {
if let content = element.value {
switch result[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(StringBox(content))
result[key] = unkeyedBox
case let stringBox as StringBox:
result[key] = UnkeyedBox([stringBox, StringBox(content)])
default:
result[key] = StringBox(content)
}
}
if hasElements || hasAttributes {
let content = element.flatten()
switch result[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(content)
result[key] = unkeyedBox
case let box? where !hasValue:
result[key] = UnkeyedBox([box, content])
default:
result[key] = content
}
}
} else {
switch result[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(NullBox())
result[key] = unkeyedBox
case let box?:
result[key] = UnkeyedBox([box, NullBox()])
default:
result[key] = NullBox()
}
}
return result
}
var elements = self.elements.reduce(storage) { $0.merge(element: $1) }

// Handle attributed unkeyed value <foo attr="bar">zap</foo>
// Value should be zap. Detect only when no other elements exist
Expand Down Expand Up @@ -287,11 +241,9 @@ struct XMLCoderElement: Equatable {

extension XMLCoderElement {
init(key: String, box: UnkeyedBox) {
let elements = box.map { box in
XMLCoderElement(key: key, box: box)
}

self.init(key: key, elements: elements)
self.init(key: key, elements: box.map {
XMLCoderElement(key: key, box: $0)
})
}

init(key: String, box: KeyedBox) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// XMLDecoder.swift
// SingleValueDecodingContainer.swift
// XMLCoder
//
// Created by Shawn Moore on 11/20/17.
Expand Down
Loading