diff options
Diffstat (limited to 'lib/pure/xmldom.nim')
-rwxr-xr-x | lib/pure/xmldom.nim | 1094 |
1 files changed, 0 insertions, 1094 deletions
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim deleted file mode 100755 index 47e94243e..000000000 --- a/lib/pure/xmldom.nim +++ /dev/null @@ -1,1094 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2010 Dominik Picheta -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - - -import strutils -## This module implements XML DOM Level 2 Core specification(http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html) - - -#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html - -#Exceptions -type - EDOMException* = object of EInvalidValue ## Base exception object for all DOM Exceptions - EDOMStringSizeErr* = object of EDOMException ## If the specified range of text does not fit into a DOMString - ## Currently not used(Since DOMString is just string) - EHierarchyRequestErr* = object of EDOMException ## If any node is inserted somewhere it doesn't belong - EIndexSizeErr* = object of EDOMException ## If index or size is negative, or greater than the allowed value - EInuseAttributeErr* = object of EDOMException ## If an attempt is made to add an attribute that is already in use elsewhere - EInvalidAccessErr* = object of EDOMException ## If a parameter or an operation is not supported by the underlying object. - EInvalidCharacterErr* = object of EDOMException ## This exception is raised when a string parameter contains an illegal character - EInvalidModificationErr* = object of EDOMException ## If an attempt is made to modify the type of the underlying object. - EInvalidStateErr* = object of EDOMException ## If an attempt is made to use an object that is not, or is no longer, usable. - ENamespaceErr* = object of EDOMException ## If an attempt is made to create or change an object in a way which is incorrect with regard to namespaces. - ENotFoundErr* = object of EDOMException ## If an attempt is made to reference a node in a context where it does not exist - ENotSupportedErr* = object of EDOMException ## If the implementation does not support the requested type of object or operation. - ENoDataAllowedErr* = object of EDOMException ## If data is specified for a node which does not support data - ENoModificationAllowedErr* = object of EDOMException ## If an attempt is made to modify an object where modifications are not allowed - ESyntaxErr* = object of EDOMException ## If an invalid or illegal string is specified. - EWrongDocumentErr* = object of EDOMException ## If a node is used in a different document than the one that created it (that doesn't support it) - -const - ElementNode* = 1 - AttributeNode* = 2 - TextNode* = 3 - CDataSectionNode* = 4 - ProcessingInstructionNode* = 7 - CommentNode* = 8 - DocumentNode* = 9 - DocumentFragmentNode* = 11 - - # Nodes which are childless - Not sure about AttributeNode - childlessObjects = {DocumentNode, AttributeNode, TextNode, - CDataSectionNode, ProcessingInstructionNode, CommentNode} - # Illegal characters - illegalChars = {'>', '<', '&', '"'} - - -type - Feature = tuple[name: string, version: string] - PDOMImplementation* = ref DOMImplementation - DOMImplementation = object - Features: seq[Feature] # Read-Only - - PNode* = ref Node - Node = object of TObject - attributes*: seq[PAttr] - childNodes*: seq[PNode] - FLocalName: string # Read-only - FNamespaceURI: string # Read-only - FNodeName: string # Read-only - nodeValue*: string - FNodeType: int # Read-only - FOwnerDocument: PDocument # Read-Only - FParentNode: PNode # Read-Only - prefix*: string # Setting this should change some values... TODO! - - PElement* = ref Element - Element = object of Node - FTagName: string # Read-only - - PCharacterData* = ref CharacterData - CharacterData = object of Node - data*: string - - PDocument* = ref Document - Document = object of Node - FImplementation: PDOMImplementation # Read-only - FDocumentElement: PElement # Read-only - - PAttr* = ref Attr - Attr = object of Node - FName: string # Read-only - FSpecified: bool # Read-only - value*: string - FOwnerElement: PElement # Read-only - - PDocumentFragment* = ref DocumentFragment - DocumentFragment = object of Node - - PText* = ref Text - Text = object of CharacterData - - PComment* = ref comment - Comment = object of CharacterData - - PCDataSection* = ref CDataSection - CDataSection = object of Text - - PProcessingInstruction* = ref ProcessingInstruction - ProcessingInstruction = object of Node - data*: string - FTarget: string # Read-only - -# DOMImplementation -proc getDOM*(): PDOMImplementation = - ## Returns a DOMImplementation - new(result) - result.Features = @[(name: "core", version: "2.0"), - (name: "core", version: "1.0"), - (name: "XML", version: "2.0")] - -proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedName: string): PDocument = - ## Creates an XML Document object of the specified type with its document element. - var doc: PDocument - new(doc) - doc.FNamespaceURI = namespaceURI - doc.FImplementation = dom - - var elTag: PElement - new(elTag) - elTag.FTagName = qualifiedName - elTag.FNodeName = qualifiedName - doc.FDocumentElement = elTag - doc.FNodeType = DocumentNode - - return doc - -proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument = - ## Creates an XML Document object of the specified type with its document element. - - # This procedure is not in the specification, it's provided for the parser. - var doc: PDocument - new(doc) - doc.FDocumentElement = n - doc.FImplementation = dom - doc.FNodeType = DocumentNode - - return doc - -proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool = - ## Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false`` - for iName, iVersion in items(dom.Features): - if iName == feature: - if version == "": - return True - else: - if iVersion == version: - return True - return False - - -# Document -# Attributes - -proc implementation*(doc: PDocument): PDOMImplementation = - return doc.FImplementation - -proc documentElement*(doc: PDocument): PElement = - return doc.FDocumentElement - -# Internal procedures -proc findNodes(nl: PNode, name: string): seq[PNode] = - # Made for getElementsByTagName - var r: seq[PNode] = @[] - if isNil(nl.childNodes): return @[] - if nl.childNodes.len() == 0: return @[] - - for i in items(nl.childNodes): - if i.FNodeType == ElementNode: - if i.FNodeName == name or name == "*": - r.add(i) - - if not isNil(i.childNodes): - if i.childNodes.len() != 0: - r.add(findNodes(i, name)) - - return r - -proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] = - # Made for getElementsByTagNameNS - var r: seq[PNode] = @[] - if isNil(nl.childNodes): return @[] - if nl.childNodes.len() == 0: return @[] - - for i in items(nl.childNodes): - if i.FNodeType == ElementNode: - if (i.FNamespaceURI == namespaceURI or namespaceURI == "*") and (i.FLocalName == localName or localName == "*"): - r.add(i) - - if not isNil(i.childNodes): - if i.childNodes.len() != 0: - r.add(findNodesNS(i, namespaceURI, localName)) - - return r - - -#Procedures -proc createAttribute*(doc: PDocument, name: string): PAttr = - ## Creates an Attr of the given name. Note that the Attr instance can then be set on an Element using the setAttributeNode method. - ## To create an attribute with a qualified name and namespace URI, use the createAttributeNS method. - - # Check if name contains illegal characters - if illegalChars in name: - raise newException(EInvalidCharacterErr, "Invalid character") - - var AttrNode: PAttr - new(AttrNode) - AttrNode.FName = name - AttrNode.FNodeName = name - AttrNode.FLocalName = nil - AttrNode.prefix = nil - AttrNode.FNamespaceURI = nil - AttrNode.value = "" - AttrNode.FSpecified = False - return AttrNode - -proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PAttr = - ## Creates an attribute of the given qualified name and namespace URI - - # Check if name contains illegal characters - if illegalChars in namespaceURI or illegalChars in qualifiedName: - raise newException(EInvalidCharacterErr, "Invalid character") - # Exceptions - if qualifiedName.contains(':'): - if namespaceURI == nil: - raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") - elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace": - raise newException(ENamespaceErr, - "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") - elif qualifiedName.split(':')[1].toLower() == "xmlns" and namespaceURI != "http://www.w3.org/2000/xmlns/": - raise newException(ENamespaceErr, - "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") - - var AttrNode: PAttr - new(AttrNode) - AttrNode.FName = qualifiedName - AttrNode.FNodeName = qualifiedName - AttrNode.FSpecified = False - AttrNode.FNamespaceURI = namespaceURI - if qualifiedName.contains(':'): - AttrNode.prefix = qualifiedName.split(':')[0] - AttrNode.FLocalName = qualifiedName.split(':')[1] - else: - AttrNode.prefix = nil - AttrNode.FLocalName = qualifiedName - AttrNode.value = "" - - AttrNode.FNodeType = AttributeNode - return AttrNode - -proc createCDATASection*(doc: PDocument, data: string): PCDATASection = - ## Creates a CDATASection node whose value is the specified string. - var CData: PCDATASection - new(CData) - CData.data = data - CData.nodeValue = data - CData.FNodeName = "#text" # Not sure about this, but this is technically a TextNode - CData.FNodeType = CDataSectionNode - return CData - -proc createComment*(doc: PDocument, data: string): PComment = - ## Creates a Comment node given the specified string. - var Comm: PComment - new(Comm) - Comm.data = data - Comm.nodeValue = data - - Comm.FNodeType = CommentNode - return Comm - -proc createDocumentFragment*(doc: PDocument): PDocumentFragment = - ## Creates an empty DocumentFragment object. - var DF: PDocumentFragment - new(DF) - return DF - -proc createElement*(doc: PDocument, tagName: string): PElement = - ## Creates an element of the type specified. - - # Check if name contains illegal characters - if illegalChars in tagName: - raise newException(EInvalidCharacterErr, "Invalid character") - - var elNode: PElement - new(elNode) - elNode.FTagName = tagName - elNode.FNodeName = tagName - elNode.FLocalName = nil - elNode.prefix = nil - elNode.FNamespaceURI = nil - elNode.childNodes = @[] - elNode.attributes = @[] - - elNode.FNodeType = ElementNode - - return elNode - -proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement = - ## Creates an element of the given qualified name and namespace URI. - if qualifiedName.contains(':'): - if namespaceURI == nil: - raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") - elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace": - raise newException(ENamespaceErr, - "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") - - # Check if name contains illegal characters - if illegalChars in namespaceURI or illegalChars in qualifiedName: - raise newException(EInvalidCharacterErr, "Invalid character") - - var elNode: PElement - new(elNode) - elNode.FTagName = qualifiedName - elNode.FNodeName = qualifiedName - if qualifiedName.contains(':'): - elNode.prefix = qualifiedName.split(':')[0] - elNode.FLocalName = qualifiedName.split(':')[1] - else: - elNode.prefix = nil - elNode.FLocalName = qualifiedName - elNode.FNamespaceURI = namespaceURI - elNode.childNodes = @[] - elNode.attributes = @[] - - elNode.FNodeType = ElementNode - - return elNode - -proc createProcessingInstruction*(doc: PDocument, target: string, data: string): PProcessingInstruction = - ## Creates a ProcessingInstruction node given the specified name and data strings. - - #Check if name contains illegal characters - if illegalChars in target: - raise newException(EInvalidCharacterErr, "Invalid character") - - var PI: PProcessingInstruction - new(PI) - PI.FTarget = target - PI.data = data - PI.FNodeType = ProcessingInstructionNode - return PI - -proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode - ## Creates a Text node given the specified string. - var txtNode: PText - new(txtNode) - txtNode.data = data - txtNode.nodeValue = data - txtNode.FNodeName = "#text" - - txtNode.FNodeType = TextNode - return txtNode - -discard """proc getElementById*(doc: PDocument, elementId: string): PElement = - ##Returns the ``Element`` whose ID is given by ``elementId``. If no such element exists, returns ``nil`` - #TODO""" - -proc getElementsByTagName*(doc: PDocument, tagName: string): seq[PNode] = - ## Returns a NodeList of all the Elements with a given tag name in - ## the order in which they are encountered in a preorder traversal of the Document tree. - var result: seq[PNode] = @[] - if doc.FDocumentElement.FNodeName == tagName or tagName == "*": - result.add(doc.FDocumentElement) - - result.add(doc.FDocumentElement.findNodes(tagName)) - return result - -proc getElementsByTagNameNS*(doc: PDocument, namespaceURI: string, localName: string): seq[PNode] = - ## Returns a NodeList of all the Elements with a given localName and namespaceURI - ## in the order in which they are encountered in a preorder traversal of the Document tree. - var result: seq[PNode] = @[] - if doc.FDocumentElement.FLocalName == localName or localName == "*": - if doc.FDocumentElement.FNamespaceURI == namespaceURI or namespaceURI == "*": - result.add(doc.FDocumentElement) - - result.add(doc.FDocumentElement.findNodesNS(namespaceURI, localName)) - return result - -proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode = - ## Imports a node from another document to this document - case importedNode.FNodeType - of AttributeNode: - var nAttr: PAttr = PAttr(importedNode) - nAttr.FOwnerDocument = doc - nAttr.FParentNode = nil - nAttr.FOwnerElement = nil - nAttr.FSpecified = True - return nAttr - of DocumentFragmentNode: - var n: PNode - new(n) - n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil - - n.FOwnerDocument = doc - n.FParentNode = nil - var tmp: seq[PNode] = n.childNodes - n.childNodes = @[] - if deep: - for i in low(tmp.len())..high(tmp.len()): - n.childNodes.add(importNode(doc, tmp[i], deep)) - - return n - of ElementNode: - var n: PNode - new(n) - n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil - - var tmpA: seq[PAttr] = n.attributes - n.attributes = @[] - # Import the Element node's attributes - for i in low(tmpA.len())..high(tmpA.len()): - n.attributes.add(PAttr(importNode(doc, tmpA[i], deep))) - # Import the childNodes - var tmp: seq[PNode] = n.childNodes - n.childNodes = @[] - if deep: - for i in low(tmp.len())..high(tmp.len()): - n.childNodes.add(importNode(doc, tmp[i], deep)) - - return n - of ProcessingInstructionNode, TextNode, CDataSectionNode, CommentNode: - var n: PNode - new(n) - n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil - return n - else: - raise newException(ENotSupportedErr, "The type of node being imported is not supported") - - -# Node -# Attributes - -proc firstChild*(n: PNode): PNode = - ## Returns this node's first child - - if n.childNodes.len() > 0: - return n.childNodes[0] - else: - return nil - -proc lastChild*(n: PNode): PNode = - ## Returns this node's last child - - if n.childNodes.len() > 0: - return n.childNodes[n.childNodes.len() - 1] - else: - return nil - -proc localName*(n: PNode): string = - ## Returns this nodes local name - - return n.FLocalName - -proc namespaceURI*(n: PNode): string = - ## Returns this nodes namespace URI - - return n.FNamespaceURI - -proc `namespaceURI=`*(n: PNode, value: string) = - n.FNamespaceURI = value - -proc nextSibling*(n: PNode): PNode = - ## Returns the next sibling of this node - - var nLow: int = low(n.FParentNode.childNodes) - var nHigh: int = high(n.FParentNode.childNodes) - for i in nLow..nHigh: - if n.FParentNode.childNodes[i] == n: - return n.FParentNode.childNodes[i + 1] - return nil - -proc nodeName*(n: PNode): string = - ## Returns the name of this node - - return n.FNodeName - -proc nodeType*(n: PNode): int = - ## Returns the type of this node - - return n.FNodeType - -proc ownerDocument*(n: PNode): PDocument = - ## Returns the owner document of this node - - return n.FOwnerDocument - -proc parentNode*(n: PNode): PNode = - ## Returns the parent node of this node - - return n.FParentNode - -proc previousSibling*(n: PNode): PNode = - ## Returns the previous sibling of this node - - var nLow: int = low(n.FParentNode.childNodes) - var nHigh: int = high(n.FParentNode.childNodes) - for i in nLow..nHigh: - if n.FParentNode.childNodes[i] == n: - return n.FParentNode.childNodes[i - 1] - return nil - -proc `prefix=`*(n: PNode, value: string) = - ## Modifies the prefix of this node - - # Setter - # Check if name contains illegal characters - if illegalChars in value: - raise newException(EInvalidCharacterErr, "Invalid character") - - if n.FNamespaceURI == nil: - raise newException(ENamespaceErr, "namespaceURI cannot be nil") - elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace": - raise newException(ENamespaceErr, - "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") - elif value.toLower() == "xmlns" and n.FNamespaceURI != "http://www.w3.org/2000/xmlns/": - raise newException(ENamespaceErr, - "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") - elif value.toLower() == "xmlns" and n.FNodeType == AttributeNode: - raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"") - - n.FNodeName = value & ":" & n.FLocalName - if n.nodeType == ElementNode: - var el: PElement = PElement(n) - el.FTagName = value & ":" & n.FLocalName - - elif n.nodeType == AttributeNode: - var attr: PAttr = PAttr(n) - attr.FName = value & ":" & n.FLocalName - -# Procedures -proc appendChild*(n: PNode, newChild: PNode) = - ## Adds the node newChild to the end of the list of children of this node. - ## If the newChild is already in the tree, it is first removed. - - # Check if n contains newChild - if not IsNil(n.childNodes): - for i in low(n.childNodes)..high(n.childNodes): - if n.childNodes[i] == newChild: - raise newException(EHierarchyRequestErr, "The node to append is already in this nodes children.") - - # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - - if n == newChild: - raise newException(EHierarchyRequestErr, "You can't add a node into itself") - - if n.nodeType in childlessObjects: - raise newException(ENoModificationAllowedErr, "Cannot append children to a childless node") - - if isNil(n.childNodes): n.childNodes = @[] - - newChild.FParentNode = n - for i in low(n.childNodes)..high(n.childNodes): - if n.childNodes[i] == newChild: - n.childNodes[i] = newChild - - n.childNodes.add(newChild) - -proc cloneNode*(n: PNode, deep: bool): PNode = - ## Returns a duplicate of this node, if ``deep`` is `true`, Element node's children are copied - case n.FNodeType - of AttributeNode: - var newNode: PAttr - new(newNode) - newNode = PAttr(n) - newNode.FSpecified = True - newNode.FOwnerElement = nil - return newNode - of ElementNode: - var newNode: PElement - new(newNode) - newNode = PElement(n) - # Import the childNodes - var tmp: seq[PNode] = n.childNodes - n.childNodes = @[] - if deep: - for i in low(tmp.len())..high(tmp.len()): - n.childNodes.add(cloneNode(tmp[i], deep)) - return newNode - else: - var newNode: PNode - new(newNode) - newNode = n - return newNode - -proc hasAttributes*(n: PNode): bool = - ## Returns whether this node (if it is an element) has any attributes. - return n.attributes.len() > 0 - -proc hasChildNodes*(n: PNode): bool = - ## Returns whether this node has any children. - return n.childNodes.len() > 0 - -proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode = - ## Inserts the node ``newChild`` before the existing child node ``refChild``. - ## If ``refChild`` is nil, insert ``newChild`` at the end of the list of children. - - # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - - for i in low(n.childNodes)..high(n.childNodes): - if n.childNodes[i] == refChild: - n.childNodes.insert(newChild, i - 1) - return - -proc isSupported*(n: PNode, feature: string, version: string): bool = - ## Tests whether the DOM implementation implements a specific - ## feature and that feature is supported by this node. - return n.FOwnerDocument.FImplementation.hasFeature(feature, version) - -proc isEmpty(s: string): bool = - - if s == "" or s == nil: - return True - for i in items(s): - if i != ' ': - return False - return True - -proc normalize*(n: PNode) = - ## Merges all seperated TextNodes together, and removes any empty TextNodes - var curTextNode: PNode = nil - var i: int = 0 - - var newChildNodes: seq[PNode] = @[] - while True: - if i >= n.childNodes.len: - break - if n.childNodes[i].nodeType == TextNode: - - #If the TextNode is empty, remove it - if PText(n.childNodes[i]).data.isEmpty(): - inc(i) - - if curTextNode == nil: - curTextNode = n.childNodes[i] - else: - PText(curTextNode).data.add(PText(n.childNodes[i]).data) - curTextNode.nodeValue.add(PText(n.childNodes[i]).data) - inc(i) - else: - newChildNodes.add(curTextNode) - newChildNodes.add(n.childNodes[i]) - curTextNode = nil - - inc(i) - n.childNodes = newChildNodes - -proc removeChild*(n: PNode, oldChild: PNode): PNode = - ## Removes the child node indicated by ``oldChild`` from the list of children, and returns it. - for i in low(n.childNodes)..high(n.childNodes): - if n.childNodes[i] == oldChild: - result = n.childNodes[i] - n.childNodes.delete(i) - return result - - raise newException(ENotFoundErr, "Node not found") - -proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode = - ## Replaces the child node ``oldChild`` with ``newChild`` in the list of children, and returns the ``oldChild`` node. - - # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - - for i in low(n.childNodes)..high(n.childNodes): - if n.childNodes[i] == oldChild: - result = n.childNodes[i] - n.childNodes[i] = newChild - return result - - raise newException(ENotFoundErr, "Node not found") - -# NamedNodeMap - -proc getNamedItem*(NList: seq[PNode], name: string): PNode = - ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil`` - for i in items(NList): - if i.nodeName() == name: - return i - return nil - -proc getNamedItem*(NList: seq[PAttr], name: string): PAttr = - ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil`` - for i in items(NList): - if i.nodeName() == name: - return i - return nil - -proc getNamedItemNS*(NList: seq[PNode], namespaceURI: string, localName: string): PNode = - ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil`` - for i in items(NList): - if i.namespaceURI() == namespaceURI and i.localName() == localName: - return i - return nil - -proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string): PAttr = - ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil`` - for i in items(NList): - if i.NamespaceURI() == namespaceURI and i.LocalName() == localName: - return i - return nil - -proc item*(NList: seq[PNode], index: int): PNode = - ## Returns the ``index`` th item in the map. - ## If ``index`` is greater than or equal to the number of nodes in this map, this returns ``nil``. - if index >= NList.len(): return nil - else: return NList[index] - -proc removeNamedItem*(NList: var seq[PNode], name: string): PNode = - ## Removes a node specified by ``name`` - ## Raises the ``ENotFoundErr`` exception, if the node was not found - for i in low(NList)..high(NList): - if NList[i].FNodeName == name: - result = NList[i] - NList.delete(i) - return result - - raise newException(ENotFoundErr, "Node not found") - -proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName: string): PNode = - ## Removes a node specified by local name and namespace URI - for i in low(NList)..high(NList): - if NList[i].FLocalName == localName and NList[i].FNamespaceURI == namespaceURI: - result = NList[i] - NList.delete(i) - return result - - raise newException(ENotFoundErr, "Node not found") - -proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode = - ## Adds ``arg`` as a ``Node`` to the ``NList`` - ## If a node with the same name is already present in this map, it is replaced by the new one. - if not isNil(NList): - if NList.len() > 0: - #Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - #Exceptions End - - var item: PNode = NList.getNamedItem(arg.NodeName()) - if item == nil: - NList.add(arg) - return nil - else: - # Node with the same name exists - var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: - index = i - break - NList[index] = arg - return item # Return the replaced node - -proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr = - ## Adds ``arg`` as a ``Node`` to the ``NList`` - ## If a node with the same name is already present in this map, it is replaced by the new one. - if not IsNil(NList): - if NList.len() > 0: - # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - - if arg.FOwnerElement != nil: - raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") - - # Exceptions end - var item: PAttr = NList.getNamedItem(arg.nodeName()) - if item == nil: - NList.add(arg) - return nil - else: - # Node with the same name exists - var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: - index = i - break - NList[index] = arg - return item # Return the replaced node - -proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode = - ## Adds a node using its ``namespaceURI`` and ``localName`` - if not IsNil(NList): - if NList.len() > 0: - # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - #Exceptions end - - var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName()) - if item == nil: - NList.add(arg) - return nil - else: - # Node with the same name exists - var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: - index = i - break - NList[index] = arg - return item # Return the replaced node - -proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr = - ## Adds a node using its ``namespaceURI`` and ``localName`` - if not isNil(NList): - if NList.len() > 0: - # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: - raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - - if arg.FOwnerElement != nil: - raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") - - # Exceptions end - var item: PAttr = NList.getNamedItemNS(arg.namespaceURI(), arg.localName()) - if item == nil: - NList.add(arg) - return nil - else: - # Node with the same name exists - var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: - index = i - break - NList[index] = arg - return item # Return the replaced node - -# CharacterData - Decided to implement this, -# Didn't add the procedures, because you can just edit .data - -# Attr -# Attributes -proc name*(a: PAttr): string = - ## Returns the name of the Attribute - - return a.FName - -proc specified*(a: PAttr): bool = - ## Specifies whether this attribute was specified in the original document - - return a.FSpecified - -proc ownerElement*(a: PAttr): PElement = - ## Returns this Attributes owner element - - return a.FOwnerElement - -# Element -# Attributes - -proc tagName*(el: PElement): string = - ## Returns the Element Tag Name - - return el.FTagName - -# Procedures -proc getAttribute*(el: PElement, name: string): string = - ## Retrieves an attribute value by ``name`` - var attribute = el.attributes.getNamedItem(name) - if attribute != nil: - return attribute.value - else: - return nil - -proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): string = - ## Retrieves an attribute value by ``localName`` and ``namespaceURI`` - var attribute = el.attributes.getNamedItemNS(namespaceURI, localName) - if attribute != nil: - return attribute.value - else: - return nil - -proc getAttributeNode*(el: PElement, name: string): PAttr = - ## Retrieves an attribute node by ``name`` - ## To retrieve an attribute node by qualified name and namespace URI, use the `getAttributeNodeNS` method - return el.attributes.getNamedItem(name) - -proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr = - ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI`` - return el.attributes.getNamedItemNS(namespaceURI, localName) - -proc getElementsByTagName*(el: PElement, name: string): seq[PNode] = - ## Returns a `NodeList` of all descendant `Elements` of ``el`` with a given tag ``name``, - ## in the order in which they are encountered in a preorder traversal of this `Element` tree - ## If ``name`` is `*`, returns all descendant of ``el`` - result = el.findNodes(name) - -proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: string): seq[PNode] = - ## Returns a `NodeList` of all the descendant Elements with a given - ## ``localName`` and ``namespaceURI`` in the order in which they are - ## encountered in a preorder traversal of this Element tree - result = el.findNodesNS(namespaceURI, localName) - -proc hasAttribute*(el: PElement, name: string): bool = - ## Returns ``true`` when an attribute with a given ``name`` is specified - ## on this element , ``false`` otherwise. - return el.attributes.getNamedItem(name) != nil - -proc hasAttributeNS*(el: PElement, namespaceURI: string, localName: string): bool = - ## Returns ``true`` when an attribute with a given ``localName`` and - ## ``namespaceURI`` is specified on this element , ``false`` otherwise - return el.attributes.getNamedItemNS(namespaceURI, localName) != nil - -proc removeAttribute*(el: PElement, name: string) = - ## Removes an attribute by ``name`` - for i in low(el.attributes)..high(el.attributes): - if el.attributes[i].FName == name: - el.attributes.delete(i) - -proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) = - ## Removes an attribute by ``localName`` and ``namespaceURI`` - for i in low(el.attributes)..high(el.attributes): - if el.attributes[i].FNamespaceURI == namespaceURI and - el.attributes[i].FLocalName == localName: - el.attributes.delete(i) - -proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr = - ## Removes the specified attribute node - ## If the attribute node cannot be found raises ``ENotFoundErr`` - for i in low(el.attributes)..high(el.attributes): - if el.attributes[i] == oldAttr: - result = el.attributes[i] - el.attributes.delete(i) - return result - - raise newException(ENotFoundErr, "oldAttr is not a member of el's Attributes") - -proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr = - ## Adds a new attribute node, if an attribute with the same `nodeName` is - ## present, it is replaced by the new one and the replaced attribute is - ## returned, otherwise ``nil`` is returned. - - # Check if newAttr is from this nodes document - if el.FOwnerDocument != newAttr.FOwnerDocument: - raise newException(EWrongDocumentErr, - "This node belongs to a different document, use importNode.") - - if newAttr.FOwnerElement != nil: - raise newException(EInuseAttributeErr, - "This attribute is in use by another element, use cloneNode") - # Exceptions end - - if isNil(el.attributes): el.attributes = @[] - return el.attributes.setNamedItem(newAttr) - -proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr = - ## Adds a new attribute node, if an attribute with the localName and - ## namespaceURI of ``newAttr`` is present, it is replaced by the new one - ## and the replaced attribute is returned, otherwise ``nil`` is returned. - - # Check if newAttr is from this nodes document - if el.FOwnerDocument != newAttr.FOwnerDocument: - raise newException(EWrongDocumentErr, - "This node belongs to a different document, use importNode.") - - if newAttr.FOwnerElement != nil: - raise newException(EInuseAttributeErr, - "This attribute is in use by another element, use cloneNode") - # Exceptions end - - if isNil(el.attributes): el.attributes = @[] - return el.attributes.setNamedItemNS(newAttr) - -proc setAttribute*(el: PElement, name: string, value: string) = - ## Adds a new attribute, as specified by ``name`` and ``value`` - ## If an attribute with that name is already present in the element, its - ## value is changed to be that of the value parameter - ## Raises the EInvalidCharacterErr if the specified ``name`` contains - ## illegal characters - var AttrNode = el.FOwnerDocument.createAttribute(name) - # Check if name contains illegal characters - if illegalChars in name: - raise newException(EInvalidCharacterErr, "Invalid character") - - discard el.setAttributeNode(AttrNode) - # Set the info later, the setAttributeNode checks - # if FOwnerElement is nil, and if it isn't it raises an exception - AttrNode.FOwnerElement = el - AttrNode.FSpecified = True - AttrNode.value = value - -proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) = - ## Adds a new attribute, as specified by ``namespaceURI``, ``localName`` - ## and ``value``. - - # Check if name contains illegal characters - if illegalChars in namespaceURI or illegalChars in localName: - raise newException(EInvalidCharacterErr, "Invalid character") - - var AttrNode = el.FOwnerDocument.createAttributeNS(namespaceURI, localName) - - discard el.setAttributeNodeNS(AttrNode) - # Set the info later, the setAttributeNode checks - # if FOwnerElement is nil, and if it isn't it raises an exception - AttrNode.FOwnerElement = el - AttrNode.FSpecified = True - AttrNode.value = value - -# Text -proc splitData*(TextNode: PText, offset: int): PText = - ## Breaks this node into two nodes at the specified offset, - ## keeping both in the tree as siblings. - - if offset > TextNode.data.len(): - raise newException(EIndexSizeErr, "Index out of bounds") - - var left: string = TextNode.data.substr(0, offset) - TextNode.data = left - var right: string = TextNode.data.substr(offset, TextNode.data.len()) - - if TextNode.FParentNode != nil: - for i in low(TextNode.FParentNode.childNodes)..high(TextNode.FParentNode.childNodes): - if TextNode.FParentNode.childNodes[i] == TextNode: - var newNode: PText = TextNode.FOwnerDocument.createTextNode(right) - TextNode.FParentNode.childNodes.insert(newNode, i) - return newNode - else: - var newNode: PText = TextNode.FOwnerDocument.createTextNode(right) - return newNode - - -# ProcessingInstruction -proc target*(PI: PProcessingInstruction): string = - ## Returns the Processing Instructions target - - return PI.FTarget - - -# --Other stuff-- -# Writer -proc addEscaped(s: string): string = - result = "" - for c in items(s): - case c - of '<': result.add("<") - of '>': result.add(">") - of '&': result.add("&") - of '"': result.add(""") - else: result.add(c) - -proc nodeToXml(n: PNode, indent: int = 0): string = - result = repeatChar(indent, ' ') & "<" & n.nodeName - for i in items(n.Attributes): - result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"") - - if n.childNodes.len() == 0: - result.add("/>") # No idea why this doesn't need a \n :O - else: - # End the beginning of this tag - result.add(">\n") - for i in items(n.childNodes): - case i.nodeType - of ElementNode: - result.add(nodeToXml(i, indent + 2)) - of TextNode: - result.add(repeatChar(indent * 2, ' ')) - result.add(addEscaped(i.nodeValue)) - of CDataSectionNode: - result.add(repeatChar(indent * 2, ' ')) - result.add("<![CDATA[" & i.nodeValue & "]]>") - of ProcessingInstructionNode: - result.add(repeatChar(indent * 2, ' ')) - result.add("<?" & PProcessingInstruction(i).target & " " & - PProcessingInstruction(i).data & " ?>") - of CommentNode: - result.add(repeatChar(indent * 2, ' ')) - result.add("<!-- " & i.nodeValue & " -->") - else: - continue - result.add("\n") - # Add the ending tag - </tag> - result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">") - -proc `$`*(doc: PDocument): string = - ## Converts a PDocument object into a string representation of it's XML - result = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" - result.add(nodeToXml(doc.documentElement)) |