@@ -1,6 +1,6 @@
//
// File.swift
// Pods
// S3DXSD
//
//
// Created by David Conner on 10/12/15.
//
@@ -9,314 +9,380 @@
import Foundation
import Ono

// TODO: Schema Validation? https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/NSXML_Concepts/Articles/CreatingXMLDoc.html
// TODO: Event Driven XML programming in iOS: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/XMLParsing/Articles/HandlingElements.html#//apple_ref/doc/uid/20002265-1001887
public enum S3DXSDType {
case MtlEnum(name: String)
case MtlDescriptorElement(name: String)
case MtlDescriptorType(name: String)
// case XSDAttribute(name: String) // do i realllly need attribute?
// case XSDAttributeGroup(name: String)
//methods to construct document selector for dependency
public func findSelector() -> String {
switch self {
case .MtlEnum(let name):
return "xs:simpleType[name=\(name)][mtl-enum=true]"
case .MtlDescriptorElement(let name):
return "xs:element[name=\(name)]"
case .MtlDescriptorType(let name):
return "xs:complexType[name=\(name)]"
// case .XSDAttributeGroup(let name):
// return "xs:attributeGroup[name=\(name)]"
}
}

public static func getXSDNode(tag: String, name: String) -> S3DXSDType {
switch tag {
// case "xs:attribute": //don't need it in parseXSD()
// return .XSDAttribute(name: name)
// case "xs:attributeGroup":
// return .XSDAttributeGroup(name: name)
case "xs:element":
return .MtlDescriptorElement(name: name)
case "xs:complexType":
return .MtlDescriptorType(name: name)
default:
// if (tag == "xs:simpleType" && mtlEnumAttr == "true")
return .MtlEnum(name: name)
}
}
}

public enum S3DXSDBaseType: String {
case XSDString = "xs:string"
case XSDInteger = "xs:integer"
case XSDFloat = "xs:float"
case XSDDouble = "xs:double"

public func convert(val: String) -> AnyObject {
switch self {
case XSDString:
return val
case XSDInteger:
return Int(val)!
case XSDFloat:
return Float(val)!
case XSDDouble:
return Double(val)!
}
}
}

// TODO: on XSDNode's
// - i can store attributes [String: XSDNode],
// - but then i run into type conflict at attribute level
// - or i can store children, but i have extra memory overhead
// TODO: evaluate protocol for S3D Node
public protocol S3DXSDNode {
var name: String { get set }
var type: S3DXSDType { get set }
var attributes: [String: S3DXSDNode] { get set }

init(type: S3DXSDType, elem: ONOXMLElement)
// func apply(elem: ONOXMLElement)
}

//public class S3DXSDAttribute: S3DXSDNode {
// public var name: String
// public var type: S3DXSDType
// // public var value: AnyObject
// public var attributes: [String: S3DXSDNode] = [:]
//
// public required init(type: S3DXSDType, elem: ONOXMLElement) {
// self.type = type
// self.name = elem.valueForAttribute("name") as! String
// }
//
//}
public class S3DMtlEnum: S3DXSDNode {
public var name: String
public var type: S3DXSDType
public var attributes: [String: S3DXSDNode] = [:]
public var children: [String: S3DXSDNode] = [:]
public var values: [String:Int] = [:]
public class S3DMtlEnum {
var name: String
var values: [String: Int] = [:] // private?
public required init(type: S3DXSDType, elem: ONOXMLElement) {
self.type = type
self.name = elem.valueForAttribute("name") as! String
parseEnumValues(elem)
}

public func parseEnumValues(elem: ONOXMLElement) {
public init(elem: ONOXMLElement) {
values = [:]
name = elem.valueForAttribute("name") as! String
let valuesSelector = "xs:restriction > xs:enumeration"
elem.enumerateElementsWithCSS(valuesSelector) { (el, idx, stop) -> Void in
let val = el.valueForAttribute("id") as! String
let key = el.valueForAttribute("value") as! String
self.values[key] = Int(val)
}
}
}

public class S3DMtlDescriptorElement: S3DXSDNode {
public var name: String
public var type: S3DXSDType
public var attributes: [String: S3DXSDNode] = [:]
public var descriptorTypeMap: [String: String] = [:]

public required init(type: S3DXSDType, elem: ONOXMLElement) {
self.type = type
self.name = elem.valueForAttribute("name") as! String
public func getValue(key: String) -> AnyObject {
return convertToEnum(key, val: values[key]!)
}
}

public class S3DMtlDescriptorType: S3DXSDNode {
public var name: String
public var type: S3DXSDType
public var attributes: [String: S3DXSDNode] = [:]

public required init(type: S3DXSDType, elem: ONOXMLElement) {
self.type = type
self.name = elem.valueForAttribute("name") as! String
public func convertToEnum(key: String, val: Int) -> AnyObject {
switch key {
case "mtlStorageAction": return MTLStorageMode(rawValue: UInt(val))!
default: val
}
}
}

public class S3DXSD {
public var xml: ONOXMLDocument?
public var enumTypes: [String:S3DMtlEnum] = [:]
public var descriptorTypes: [String:S3DMtlDescriptorType] = [:]
public var descriptorElements: [String:S3DMtlDescriptorElement] = [:]
var xsd: ONOXMLDocument?
var enumTypes: [String: S3DMtlEnum] = [:]

public init(data: NSData!) {
xml = try! ONOXMLDocument(data: data)
resetNodes()
public init(data: NSData) {
xsd = try! ONOXMLDocument(data: data)
}

public class func readXSD(filename: String) -> NSData {
let podBundle = NSBundle(forClass: S3DXSD.self)
let bundleUrl = podBundle.URLForResource("Spectra", withExtension: "bundle")
let bundle = NSBundle(URL: bundleUrl!)
let path = bundle!.pathForResource("Spectra3D", ofType: "xsd")
let path = bundle!.pathForResource(filename, ofType: "xsd")
let data = NSData(contentsOfFile: path!)
return data!
}

// public class func readXSDString(string: String) -> NSData {
//
// }
public func resetNodes() {
enumTypes = [:]
descriptorTypes = [:]
descriptorElements = [:]
}

public func parseXSD() {
for (elem) in xml!.rootElement.children {
var el = elem as! ONOXMLElement
let name = el.valueForAttribute("name") as! String
let mtlEnumAttr = elem.valueForAttribute("mtl-enum") as? String // TODO: cast as bool
let nodeType = S3DXSDType.getXSDNode(el.tag, name: name)
if let node = getNode(nodeType) {
} else {
let node = createNode(nodeType, elem: el)
}

// - check for node definition already
// let nodeDefinition = getNodeDefinition(el)
// - if it exists, skip to next node
// if (!nodeExists(parsedNodes, nodeDefinition: nodeDefinition)) {
// loadNode(parsedNodes, elem: el, nodeDefinition: nodeDefinition)
// }
// - if it doesn't, parse together a new one and assemble it's attributes
// - if one of those attributes has an unknown type dependency
// - break out and load that specific one and it's dependencies recursively
// - when all root nodes are done, all type definitions should have been read in
// return nodes
}
}

public func getNode(nodeType: S3DXSDType) -> S3DXSDNode? {
switch nodeType {
case .MtlEnum(let name):
return enumTypes[name]
case .MtlDescriptorElement(let name):
return descriptorElements[name]
case .MtlDescriptorType(let name):
return descriptorTypes[name]
// case .XSDAttributeGroup(let name):
// return attributeGroups[name]
}
}

public func createNode(nodeType: S3DXSDType, elem: ONOXMLElement) -> S3DXSDNode {
switch nodeType {
case .MtlEnum:
return S3DMtlEnum(type: nodeType, elem: elem)
case .MtlDescriptorElement:
return S3DMtlDescriptorElement(type: nodeType, elem: elem)
case .MtlDescriptorType:
return S3DMtlDescriptorType(type: nodeType, elem: elem)
// case .XSDAttributeGroup:
// return S3DXSDAttributeGroup(type: nodeType, elem: elem)

public func parseEnumTypes() {
let enumTypesSelector = "xs:simpleType[mtl-enum=true]"
xsd!.enumerateElementsWithCSS(enumTypesSelector) { (elem, idx, stop) -> Void in
let enumType = S3DMtlEnum(elem: elem)
self.enumTypes[enumType.name] = enumType
}
}

// public func getNodeDefinition(elem: ONOXMLElement) -> [String:String] {
// let tag = elem.tag
// let name = elem.valueForAttribute("name") as! String
// let typeAttr = elem.valueForAttribute("type") as! String
// let mtlEnumAttr = elem.valueForAttribute("mtl-enum") as? String // TODO: cast as bool
//
// var type: S3DXSDType = .MtlDescriptorType
// if (tag == "xs:element") {
// type = .MtlDescriptorElement
// } else {
// if (tag == "xs:simpleType" && mtlEnumAttr == "true") {
// type = .MtlEnum
// } else {
// if (tag == "xs:attributeGroup") {
// type = .MtlAttributeGroup
// }
// }
// }
//
// return ["name": name, "type": type.rawValue]
// }
// public func loadNode(var nodes: [S3DXSDType:[String:AnyObject]], elem: ONOXMLElement, nodeDefinition: [String: String]) -> [S3DXSDType:[String:AnyObject]] {
//
// // search for the specific node
// // - load it, attach them to the nodesIn function
// // - and recursively pass unknown node definitions to next level
//
// // TODO: how to load attributes?
//
// let nodeType = S3DXSDType(rawValue: nodeDefinition["type"]!)!
// switch nodeType {
// case .MtlEnum:
// let newNode = S3DMtlEnum(elem: elem)
// nodes[nodeType]![nodeDefinition["name"]!] = newNode
// break
// case .MtlAttributeGroup:
// let newNode = S3DMtlAttributeGroup(elem: elem)
// nodes[nodeType]![nodeDefinition["name"]!] = newNode
// break
// case .MtlDescriptorElement:
// break
// case .MtlDescriptorType:
// break
// }
//
// return nodes
// }
// public func nodeExists(parsedNodes: [S3DXSDType:[String:AnyObject]], nodeDefinition: [String:String]) -> Bool {
// let type = S3DXSDType(rawValue: nodeDefinition["type"]!)!
// let name = nodeDefinition["name"]!
// if let _ = parsedNodes[type]![name] {
// return true
// } else {
// return false
// }
// }
//
// public func findNode(nodeDefinition: [String:String]) -> ONOXMLElement? {
// // - construct selector to find element
// // - return it
// return xml!.rootElement
// }
//
// public func parseEnumTypes() {
// let enumTypesSelector = "xs:simpleType[mtl-enum=true]"
// xml!.enumerateElementsWithCSS(enumTypesSelector) { (elem, idx, stop) -> Void in
// let enumType = S3DMtlEnum(elem: elem)
// self.enumTypes[enumType.name] = enumType
// }
// }
//
// public func parseAttributeGroups() {
// let attributeGroupsSelector = "xs:attributeGroup[name]"
// xml!.enumerateElementsWithCSS(attributeGroupsSelector) { (elem, idx, stop) -> Void in
// let attrGroup = S3DMtlAttributeGroup(elem: elem)
// self.attributeGroups[attrGroup.name] = attrGroup
// }
// }
//
// public func parseDescriptorTypes() {
// // TODO: force XSD to process in stages by using multiple complexTypesSelector's
// let complexTypesSelector = "xs:complexType"
// xml!.enumerateElementsWithCSS(complexTypesSelector) { (elem, idx, stop) -> Void in
// let descriptorType = S3DMtlDescriptorType(elem: elem)
// self.descriptorTypes[descriptorType.name] = descriptorType
// }
// }
}

//// TODO: Schema Validation? https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/NSXML_Concepts/Articles/CreatingXMLDoc.html
//// TODO: Event Driven XML programming in iOS: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/XMLParsing/Articles/HandlingElements.html#//apple_ref/doc/uid/20002265-1001887
//public enum S3DXSDNodeType {
// case XSDMtlEnum
// case XSDElement
// case XSDSimpleType
// case XSDComplexType
//// case XSDAttribute(name: String) // do i realllly need attribute?
//// case XSDAttributeGroup(name: String)
//
// //methods to construct document selector for dependency
//
// //TODO: i might not need this, if i load everything lazily
// public func findSelector(name: String) -> String {
// switch self {
// case .XSDMtlEnum:
// return "xs:simpleType[name=\(name)][mtl-enum=true]"
// case .XSDElement:
// return "xs:element[name=\(name)]"
// case .XSDSimpleType:
// return "xs:simpleType[name]\(name)][mtl-enum=false]"
// case .XSDComplexType:
// return "xs:complexType[name=\(name)]"
//// case .XSDAttributeGroup(let name):
//// return "xs:attributeGroup[name=\(name)]"
// }
// }
//
// public static func getXSDNodeType(elem: ONOXMLElement) -> S3DXSDNodeType {
// let tag = elem.tag
// let name = elem.valueForAttribute("name") as! String
// let typeAttr = elem.valueForAttribute("type") as! String
// let mtlEnumAttr:String = elem.valueForAttribute("mtl-enum") as? String ?? ""
//
// switch (tag, mtlEnumAttr) {
//// case "xs:attribute": //don't need it in parseXSD()
//// return .XSDAttribute(name: name)
//// case "xs:attributeGroup":
//// return .XSDAttributeGroup(name: name)
// case ("xs:element", _):
// return .XSDElement
// case ("xs:complexType", _):
// return .XSDComplexType
// case ("xs:simpleType", "true"):
// return .XSDMtlEnum
// case ("xs:simpleType", _):
// return .XSDSimpleType
// }
// }
//}
//
//public enum S3DXSDBaseType: String {
// case XSDString = "xs:string"
// case XSDInteger = "xs:integer"
// case XSDFloat = "xs:float"
// case XSDDouble = "xs:double"
//
// public func convert(val: String) -> AnyObject {
// switch self {
// case XSDString:
// return val
// case XSDInteger:
// return Int(val)!
// case XSDFloat:
// return Float(val)!
// case XSDDouble:
// return Double(val)!
// }
// }
//}
//
//// TODO: on XSDNode's
//// - i can store attributes [String: XSDNode],
//// - but then i run into type conflict at attribute level
//// - or i can store children, but i have extra memory overhead
//
//// TODO: evaluate protocol for S3D Node
//public protocol S3DXSDNode {
// var name: String { get set }
// var type: S3DXSDNodeType { get set }
// var attributes: [String: S3DXSDNode] { get set }
//// var children: [String: S3DXSDNode] { get set }
//
// init(type: S3DXSDNodeType, elem: ONOXMLElement)
//// func apply(elem: ONOXMLElement)
//}
//
////public class S3DXSDAttribute: S3DXSDNode {
//// public var name: String
//// public var type: S3DXSDType
//// // public var value: AnyObject
//// public var attributes: [String: S3DXSDNode] = [:]
////
//// public required init(type: S3DXSDType, elem: ONOXMLElement) {
//// self.type = type
//// self.name = elem.valueForAttribute("name") as! String
//// }
////}
//
//public class S3DXSDMtlEnum: S3DXSDNode {
// public var name: String
// public var type: S3DXSDNodeType
// public var attributes: [String: S3DXSDNode] = [:]
//// public var children: [String: S3DXSDNode] = [:]
// public var values: [String:Int] = [:]
//
// public required init(type: S3DXSDNodeType, elem: ONOXMLElement) {
// self.type = type
// self.name = elem.valueForAttribute("name") as! String
// parseEnumValues(elem)
// }
//
// public func parseEnumValues(elem: ONOXMLElement) {
// values = [:]
// let valuesSelector = "xs:restriction > xs:enumeration"
// elem.enumerateElementsWithCSS(valuesSelector) { (el, idx, stop) -> Void in
// let val = el.valueForAttribute("id") as! String
// let key = el.valueForAttribute("value") as! String
// self.values[key] = Int(val)
// }
// }
//}
//
//public class S3DXSDElement: S3DXSDNode {
// public var name: String
// public var type: S3DXSDNodeType
// public var attributes: [String: S3DXSDNode] = [:]
//
// public required init(type: S3DXSDNodeType, elem: ONOXMLElement) {
// self.type = type
// self.name = elem.valueForAttribute("name") as! String
// let valueType = elem.valueForAttribute("type") as! String
//
//// self.descriptorType = S3DXSDType.MtlDescriptorType(name: descType)
// }
//}
//
//public class S3DXSDComplexType: S3DXSDNode {
// public var name: String
// public var type: S3DXSDNodeType
// public var attributes: [String: S3DXSDNode] = [:]
// public var children: [String: S3DXSDNode] = [:]
//
// public required init(type: S3DXSDNodeType, elem: ONOXMLElement) {
// self.type = type
// self.name = elem.valueForAttribute("name") as! String
// }
//}
//
//public class S3DXSD {
// public var xml: ONOXMLDocument?
// public var enumTypes: [String:S3DMtlEnum] = [:]
// public var descriptorTypes: [String:S3DMtlDescriptorType] = [:]
// public var descriptorElements: [String:S3DXSDElement] = [:]
//
// public init(data: NSData!) {
// xml = try! ONOXMLDocument(data: data)
// resetNodes()
// }
//
// public class func readXSD(filename: String) -> NSData {
// let podBundle = NSBundle(forClass: S3DXSD.self)
// let bundleUrl = podBundle.URLForResource("Spectra", withExtension: "bundle")
// let bundle = NSBundle(URL: bundleUrl!)
// let path = bundle!.pathForResource("Spectra3D", ofType: "xsd")
// let data = NSData(contentsOfFile: path!)
// return data!
// }
//
// // public class func readXSDString(string: String) -> NSData {
// //
// // }
//
// public func resetNodes() {
// enumTypes = [:]
// descriptorTypes = [:]
// descriptorElements = [:]
// }
//
// public func parseXSD() {
// for (elem) in xml!.rootElement.children {
// var el = elem as! ONOXMLElement
// let name = el.valueForAttribute("name") as! String
// let mtlEnumAttr = elem.valueForAttribute("mtl-enum") as? String // TODO: cast as bool
//
// let nodeType = S3DXSDType.getXSDNode(el.tag, name: name)
// if let node = getNode(nodeType) {
// } else {
// let node = createNode(nodeType, elem: el)
// }
//
// // - check for node definition already
// // let nodeDefinition = getNodeDefinition(el)
//
// // - if it exists, skip to next node
// // if (!nodeExists(parsedNodes, nodeDefinition: nodeDefinition)) {
// // loadNode(parsedNodes, elem: el, nodeDefinition: nodeDefinition)
// // }
//
// // - if it doesn't, parse together a new one and assemble it's attributes
// // - if one of those attributes has an unknown type dependency
// // - break out and load that specific one and it's dependencies recursively
// // - when all root nodes are done, all type definitions should have been read in
// // return nodes
// }
// }
//
// public func getNode(nodeType: S3DXSDType) -> S3DXSDNode? {
// switch nodeType {
// case .MtlEnum(let name):
// return enumTypes[name]
// case .MtlDescriptorElement(let name):
// return descriptorElements[name]
// case .MtlDescriptorType(let name):
// return descriptorTypes[name]
//// case .XSDAttributeGroup(let name):
//// return attributeGroups[name]
// }
// }
//
// public func createNode(nodeType: S3DXSDType, elem: ONOXMLElement) -> S3DXSDNode {
// switch nodeType {
// case .MtlEnum:
// return S3DMtlEnum(type: nodeType, elem: elem)
// case .MtlDescriptorElement:
// return S3DMtlDescriptorElement(type: nodeType, elem: elem)
// case .MtlDescriptorType:
// return S3DMtlDescriptorType(type: nodeType, elem: elem)
//// case .XSDAttributeGroup:
//// return S3DXSDAttributeGroup(type: nodeType, elem: elem)
// }
// }
//
// // public func getNodeDefinition(elem: ONOXMLElement) -> [String:String] {
// // let tag = elem.tag
// // let name = elem.valueForAttribute("name") as! String
// // let typeAttr = elem.valueForAttribute("type") as! String
// // let mtlEnumAttr = elem.valueForAttribute("mtl-enum") as? String // TODO: cast as bool
// //
// // var type: S3DXSDType = .MtlDescriptorType
// // if (tag == "xs:element") {
// // type = .MtlDescriptorElement
// // } else {
// // if (tag == "xs:simpleType" && mtlEnumAttr == "true") {
// // type = .MtlEnum
// // } else {
// // if (tag == "xs:attributeGroup") {
// // type = .MtlAttributeGroup
// // }
// // }
// // }
// //
// // return ["name": name, "type": type.rawValue]
// // }
//
// // public func loadNode(var nodes: [S3DXSDType:[String:AnyObject]], elem: ONOXMLElement, nodeDefinition: [String: String]) -> [S3DXSDType:[String:AnyObject]] {
// //
// // // search for the specific node
// // // - load it, attach them to the nodesIn function
// // // - and recursively pass unknown node definitions to next level
// //
// // // TODO: how to load attributes?
// //
// // let nodeType = S3DXSDType(rawValue: nodeDefinition["type"]!)!
// // switch nodeType {
// // case .MtlEnum:
// // let newNode = S3DMtlEnum(elem: elem)
// // nodes[nodeType]![nodeDefinition["name"]!] = newNode
// // break
// // case .MtlAttributeGroup:
// // let newNode = S3DMtlAttributeGroup(elem: elem)
// // nodes[nodeType]![nodeDefinition["name"]!] = newNode
// // break
// // case .MtlDescriptorElement:
// // break
// // case .MtlDescriptorType:
// // break
// // }
// //
// // return nodes
// // }
//
// // public func nodeExists(parsedNodes: [S3DXSDType:[String:AnyObject]], nodeDefinition: [String:String]) -> Bool {
// // let type = S3DXSDType(rawValue: nodeDefinition["type"]!)!
// // let name = nodeDefinition["name"]!
// // if let _ = parsedNodes[type]![name] {
// // return true
// // } else {
// // return false
// // }
// // }
// //
// // public func findNode(nodeDefinition: [String:String]) -> ONOXMLElement? {
// // // - construct selector to find element
// // // - return it
// // return xml!.rootElement
// // }
// //
// // public func parseEnumTypes() {
// // let enumTypesSelector = "xs:simpleType[mtl-enum=true]"
// // xml!.enumerateElementsWithCSS(enumTypesSelector) { (elem, idx, stop) -> Void in
// // let enumType = S3DMtlEnum(elem: elem)
// // self.enumTypes[enumType.name] = enumType
// // }
// // }
// //
// // public func parseAttributeGroups() {
// // let attributeGroupsSelector = "xs:attributeGroup[name]"
// // xml!.enumerateElementsWithCSS(attributeGroupsSelector) { (elem, idx, stop) -> Void in
// // let attrGroup = S3DMtlAttributeGroup(elem: elem)
// // self.attributeGroups[attrGroup.name] = attrGroup
// // }
// // }
// //
// // public func parseDescriptorTypes() {
// // // TODO: force XSD to process in stages by using multiple complexTypesSelector's
// // let complexTypesSelector = "xs:complexType"
// // xml!.enumerateElementsWithCSS(complexTypesSelector) { (elem, idx, stop) -> Void in
// // let descriptorType = S3DMtlDescriptorType(elem: elem)
// // self.descriptorTypes[descriptorType.name] = descriptorType
// // }
// // }
//}