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/Sources/Tree/XMLTree.swift b/Sources/Tree/XMLTree.swift new file mode 100644 index 0000000..d851c90 --- /dev/null +++ b/Sources/Tree/XMLTree.swift @@ -0,0 +1,73 @@ +// +// 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. + public 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(options: [.nodeCompactEmptyElement, .nodePrettyPrint, .documentTidyXML]) + } + + + /// 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 { + + 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 { + 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) + } + } +} diff --git a/Tests/TreeTests/XMLTreeTests.swift b/Tests/TreeTests/XMLTreeTests.swift new file mode 100644 index 0000000..185253a --- /dev/null +++ b/Tests/TreeTests/XMLTreeTests.swift @@ -0,0 +1,40 @@ +// +// XMLTreeTests.swift +// +// +// 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"))) + let son = Node(TestElement(name: "child", nickName: "son")) + 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() + + } + + struct TestElement: Identifiable { + var id: String + var nickName:String? + + init(name: String, nickName: String? = nil) { + self.id = name + self.nickName = nickName + } + } +} +