From 4f346183cc221b4b09bcf4b6cf26fcc4050dc9db Mon Sep 17 00:00:00 2001 From: Joshua Freilich Date: Sat, 9 Mar 2024 00:26:51 -0500 Subject: [PATCH 1/7] XMLTree.swift: Converts tree structure into XML data. --- Sources/Tree/XMLTree.swift | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Sources/Tree/XMLTree.swift diff --git a/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift new file mode 100644 index 0000000..7bd0a90 --- /dev/null +++ b/Sources/Tree/XMLTree.swift @@ -0,0 +1,67 @@ +// +// XMLTree.swift +// Tree +// +// Created by joshydotpoo on 3/8/2024. +// + +import Foundation + +public struct XMLTree { + + private(set) var data:Data? + + let property:KeyPath + let attributes:[String: KeyPath] + + /// XMLTree converts the tree into an XML file. + /// - Parameters: + /// - root: root node of the tree + /// - property: string property of generic Element used as the tag name + /// - attributes: dictionary of string property pairs, where the string is the attribute name and the property is used for attribute value. + init(root:Node, + using property:KeyPath, + assigning attributes:[String: KeyPath] = [:]) + { + self.property = property + self.attributes = attributes + + let document = XMLDocument() + document.addChild(toXML(node: root)) + self.data = document.xmlData + } + + + /// Recursive function that iterates through all the child nodes and assigns attribute values + /// - Parameter node: parent node element + /// - Returns: XMLElement of the parent element with all children XMLElements appended. + private func toXML(node:Node) -> XMLElement { + let branchElement = XMLElement(name: node.element[keyPath: property]) + for (attributeName, attributeValue) in attributes { + let attributeNode:XMLNode = XMLNode.attribute( + withName: attributeName, + stringValue: node.element[keyPath: attributeValue] + ) as! XMLNode + branchElement.addAttribute(attributeNode) + } + + for childNode in node.children { + branchElement.addChild(toXML(node: childNode)) + } + + return branchElement + } + + + /// For testing purposes only, saves data to test.xml in document directory. + public func save() { + do { + var url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + url.appendPathComponent("test.xml") + print(url.relativePath) + FileManager.default.createFile(atPath: url.relativePath, contents: self.data) + } catch { + print(error.localizedDescription) + } + } +} From 22d23037bcc99513e7d4449628a27275aa28d82a Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sat, 9 Mar 2024 16:18:32 -0500 Subject: [PATCH 2/7] Changed init to public --- Sources/Tree/XMLTree.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift index 7bd0a90..e84072e 100644 --- a/Sources/Tree/XMLTree.swift +++ b/Sources/Tree/XMLTree.swift @@ -19,7 +19,7 @@ public struct XMLTree { /// - root: root node of the tree /// - property: string property of generic Element used as the tag name /// - attributes: dictionary of string property pairs, where the string is the attribute name and the property is used for attribute value. - init(root:Node, + public init(root:Node, using property:KeyPath, assigning attributes:[String: KeyPath] = [:]) { From 9a036e05839b8f9a424f48236c4a1dd61f4d1f62 Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sat, 9 Mar 2024 17:19:38 -0500 Subject: [PATCH 3/7] Added a check if the attribute's assigned are available in that object, doesn't add them if they are nil. --- Sources/Tree/XMLTree.swift | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift index e84072e..18dc353 100644 --- a/Sources/Tree/XMLTree.swift +++ b/Sources/Tree/XMLTree.swift @@ -38,11 +38,14 @@ public struct XMLTree { private func toXML(node:Node) -> XMLElement { let branchElement = XMLElement(name: node.element[keyPath: property]) for (attributeName, attributeValue) in attributes { - let attributeNode:XMLNode = XMLNode.attribute( - withName: attributeName, - stringValue: node.element[keyPath: attributeValue] - ) as! XMLNode - branchElement.addAttribute(attributeNode) + let value:String? = node.element[keyPath: attributeValue] + if(value != nil) { + let attributeNode:XMLNode = XMLNode.attribute( + withName: attributeName, + stringValue: node.element[keyPath: attributeValue] + ) as! XMLNode + branchElement.addAttribute(attributeNode) + } } for childNode in node.children { From 953639e8f5273023fd28864ed8f1b4e2a85070f9 Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sun, 10 Mar 2024 18:44:46 -0400 Subject: [PATCH 4/7] Needed to change KeyPath to take String? to accept nil values --- Tests/TreeTests/XMLTreeTests.swift | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Tests/TreeTests/XMLTreeTests.swift diff --git a/Tests/TreeTests/XMLTreeTests.swift b/Tests/TreeTests/XMLTreeTests.swift new file mode 100644 index 0000000..0b954cc --- /dev/null +++ b/Tests/TreeTests/XMLTreeTests.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Josh Freilich on 3/10/24. +// + +import Foundation From abb8b67f272ca68fae6d2e7a34660c2b2b1e984d Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sun, 10 Mar 2024 18:49:51 -0400 Subject: [PATCH 5/7] Changed Keypath to accept a String? --- Sources/Tree/XMLTree.swift | 20 +++++++++---------- Tests/TreeTests/XMLTreeTests.swift | 32 +++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift index 18dc353..ef4c8cb 100644 --- a/Sources/Tree/XMLTree.swift +++ b/Sources/Tree/XMLTree.swift @@ -12,7 +12,7 @@ public struct XMLTree { private(set) var data:Data? let property:KeyPath - let attributes:[String: KeyPath] + let attributes:[String: KeyPath] /// XMLTree converts the tree into an XML file. /// - Parameters: @@ -21,7 +21,7 @@ public struct XMLTree { /// - attributes: dictionary of string property pairs, where the string is the attribute name and the property is used for attribute value. public init(root:Node, using property:KeyPath, - assigning attributes:[String: KeyPath] = [:]) + assigning attributes:[String: KeyPath] = [:]) { self.property = property self.attributes = attributes @@ -38,14 +38,14 @@ public struct XMLTree { private func toXML(node:Node) -> XMLElement { let branchElement = XMLElement(name: node.element[keyPath: property]) for (attributeName, attributeValue) in attributes { - let value:String? = node.element[keyPath: attributeValue] - if(value != nil) { - let attributeNode:XMLNode = XMLNode.attribute( - withName: attributeName, - stringValue: node.element[keyPath: attributeValue] - ) as! XMLNode - branchElement.addAttribute(attributeNode) - } + + if let value:String = node.element[keyPath: attributeValue] { + let attributeNode:XMLNode = XMLNode.attribute( + withName: attributeName, + stringValue: value + ) as! XMLNode + branchElement.addAttribute(attributeNode) + } } for childNode in node.children { diff --git a/Tests/TreeTests/XMLTreeTests.swift b/Tests/TreeTests/XMLTreeTests.swift index 0b954cc..1f44ee3 100644 --- a/Tests/TreeTests/XMLTreeTests.swift +++ b/Tests/TreeTests/XMLTreeTests.swift @@ -1,8 +1,34 @@ // -// File.swift -// +// XMLTreeTests.swift // -// Created by Josh Freilich on 3/10/24. +// +// Created by joshydotpoo on 3/10/24. // import Foundation +import XCTest +@testable import Tree + +final class XMLTreeTests: XCTestCase { + + func testNilAttribute() { + let root = Node(TestElement(name: "root")) + root.append(child: Node(TestElement(name: "child"))) + root.append(child: Node(TestElement(name: "child", nickName: "son"))) + + let xmltree = XMLTree(root: root, using: \TestElement.id, assigning: ["nickname": \TestElement.nickName]) + xmltree.save() + + } + + struct TestElement: Identifiable { + var id: String + var nickName:String? + + init(name: String, nickName: String? = nil) { + self.id = name + self.nickName = nickName + } + } +} + From aacefaa01affdf93322620852f89435cf38ef1f1 Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sun, 10 Mar 2024 19:36:57 -0400 Subject: [PATCH 6/7] Added: .nodeCompactEmptyElement, .nodePrettyPrint, .documentTidyXML options to make xml more readable. --- Sources/Tree/XMLTree.swift | 5 ++++- Tests/TreeTests/XMLTreeTests.swift | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift index ef4c8cb..d851c90 100644 --- a/Sources/Tree/XMLTree.swift +++ b/Sources/Tree/XMLTree.swift @@ -27,8 +27,9 @@ public struct XMLTree { self.attributes = attributes let document = XMLDocument() + document.addChild(toXML(node: root)) - self.data = document.xmlData + self.data = document.xmlData(options: [.nodeCompactEmptyElement, .nodePrettyPrint, .documentTidyXML]) } @@ -36,7 +37,9 @@ public struct XMLTree { /// - Parameter node: parent node element /// - Returns: XMLElement of the parent element with all children XMLElements appended. private func toXML(node:Node) -> XMLElement { + let branchElement = XMLElement(name: node.element[keyPath: property]) + for (attributeName, attributeValue) in attributes { if let value:String = node.element[keyPath: attributeValue] { diff --git a/Tests/TreeTests/XMLTreeTests.swift b/Tests/TreeTests/XMLTreeTests.swift index 1f44ee3..ad7b507 100644 --- a/Tests/TreeTests/XMLTreeTests.swift +++ b/Tests/TreeTests/XMLTreeTests.swift @@ -14,7 +14,9 @@ final class XMLTreeTests: XCTestCase { func testNilAttribute() { let root = Node(TestElement(name: "root")) root.append(child: Node(TestElement(name: "child"))) - root.append(child: Node(TestElement(name: "child", nickName: "son"))) + let son = Node(TestElement(name: "child", nickName: "son")) + root.append(child: son) + son.append(child: Node(TestElement(name: "grandchil", nickName: "grandson"))) let xmltree = XMLTree(root: root, using: \TestElement.id, assigning: ["nickname": \TestElement.nickName]) xmltree.save() From c3578335ff3fd2ef99ce8cb5bb2c5afc2819bef4 Mon Sep 17 00:00:00 2001 From: Josh Freilich Date: Sun, 10 Mar 2024 20:10:12 -0400 Subject: [PATCH 7/7] Changed element to a var instead of a let, allows you to be able to change the values when iterating --- Sources/Tree/Node.swift | 2 +- Tests/TreeTests/XMLTreeTests.swift | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Tree/Node.swift b/Sources/Tree/Node.swift index 9fe6c03..8cb9ac4 100644 --- a/Sources/Tree/Node.swift +++ b/Sources/Tree/Node.swift @@ -20,7 +20,7 @@ public final class Node: Identifiable { /// /// This value can be any type that conforms to `Identifiable`. /// - public let element: Element + public var element: Element /// The parent node for this node. /// diff --git a/Tests/TreeTests/XMLTreeTests.swift b/Tests/TreeTests/XMLTreeTests.swift index ad7b507..185253a 100644 --- a/Tests/TreeTests/XMLTreeTests.swift +++ b/Tests/TreeTests/XMLTreeTests.swift @@ -18,6 +18,10 @@ final class XMLTreeTests: XCTestCase { root.append(child: son) son.append(child: Node(TestElement(name: "grandchil", nickName: "grandson"))) + for node in root.depthFirst { + node.element.nickName = "testing" + } + let xmltree = XMLTree(root: root, using: \TestElement.id, assigning: ["nickname": \TestElement.nickName]) xmltree.save()