Skip to content
Open
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
2 changes: 1 addition & 1 deletion Sources/Tree/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public final class Node<Element: Identifiable>: 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.
///
Expand Down
73 changes: 73 additions & 0 deletions Sources/Tree/XMLTree.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// XMLTree.swift
// Tree
//
// Created by joshydotpoo on 3/8/2024.
//

import Foundation

public struct XMLTree<Element:Identifiable> {

private(set) var data:Data?

let property:KeyPath<Element, String>
let attributes:[String: KeyPath<Element, String?>]

/// 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<Element>,
using property:KeyPath<Element, String>,
assigning attributes:[String: KeyPath<Element, String?>] = [:])
{
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<Element>) -> 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)
}
}
}
40 changes: 40 additions & 0 deletions Tests/TreeTests/XMLTreeTests.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
}