summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2014-08-01 02:28:51 +0200
committerAndreas Rumpf <rumpf_a@web.de>2014-08-01 02:28:51 +0200
commitee61a39cff88c361e8d5be978c570d50c4d991a5 (patch)
tree0913348067e7bdf17dbf5fa39e06b59e8d0ccdc9 /lib
parentf43ec61568b42eb540fc5ee0f3e92ad12c7138b4 (diff)
parentdc756a76187aa94c9eca18d070a9710ac4193218 (diff)
downloadNim-ee61a39cff88c361e8d5be978c570d50c4d991a5.tar.gz
Merge pull request #1430 from def-/xmldom-nil
Add some nil checks for xmldom (and clean up a bit)
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/xmldom.nim435
1 files changed, 231 insertions, 204 deletions
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 47e94243e..d63b6c5dd 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -45,7 +45,7 @@ const
   DocumentFragmentNode* = 11
 
   # Nodes which are childless - Not sure about AttributeNode
-  childlessObjects = {DocumentNode, AttributeNode, TextNode, 
+  childlessObjects = {DocumentNode, AttributeNode, TextNode,
     CDataSectionNode, ProcessingInstructionNode, CommentNode}
   # Illegal characters
   illegalChars = {'>', '<', '&', '"'}
@@ -69,21 +69,21 @@ type
     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  
+
+  PAttr* = ref Attr
   Attr = object of Node
     FName: string # Read-only
     FSpecified: bool # Read-only
@@ -95,13 +95,13 @@ type
 
   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
@@ -111,8 +111,8 @@ type
 proc getDOM*(): PDOMImplementation =
   ## Returns a DOMImplementation
   new(result)
-  result.Features = @[(name: "core", version: "2.0"), 
-                      (name: "core", version: "1.0"), 
+  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 =
@@ -121,28 +121,28 @@ proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedNam
   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):
@@ -157,11 +157,11 @@ proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = "")
 
 # Document
 # Attributes
-  
+
 proc implementation*(doc: PDocument): PDOMImplementation =
   return doc.FImplementation
-  
-proc documentElement*(doc: PDocument): PElement = 
+
+proc documentElement*(doc: PDocument): PElement =
   return doc.FDocumentElement
 
 # Internal procedures
@@ -175,13 +175,13 @@ proc findNodes(nl: PNode, name: string): seq[PNode] =
     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] = @[]
@@ -192,23 +192,23 @@ proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode]
     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. 
-  
+  ## 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
@@ -222,21 +222,21 @@ proc createAttribute*(doc: PDocument, name: string): PAttr =
 
 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:
+    if isNil(namespaceURI):
       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, 
+      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, 
+      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
@@ -250,7 +250,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     AttrNode.prefix = nil
     AttrNode.FLocalName = qualifiedName
   AttrNode.value = ""
-  
+
   AttrNode.FNodeType = AttributeNode
   return AttrNode
 
@@ -265,12 +265,12 @@ proc createCDATASection*(doc: PDocument, data: string): PCDATASection =
   return CData
 
 proc createComment*(doc: PDocument, data: string): PComment =
-  ## Creates a Comment node given the specified string. 
+  ## Creates a Comment node given the specified string.
   var Comm: PComment
   new(Comm)
   Comm.data = data
   Comm.nodeValue = data
-  
+
   Comm.FNodeType = CommentNode
   return Comm
 
@@ -282,11 +282,11 @@ proc createDocumentFragment*(doc: PDocument): PDocumentFragment =
 
 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
@@ -296,24 +296,24 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
   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:
+    if isNIl(namespaceURI):
       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, 
+      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
@@ -327,18 +327,18 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin
   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. 
-  
+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
@@ -347,13 +347,13 @@ proc createProcessingInstruction*(doc: PDocument, target: string, data: string):
   return PI
 
 proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode
-  ## Creates a Text node given the specified string. 
+  ## 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
 
@@ -363,22 +363,22 @@ discard """proc getElementById*(doc: PDocument, elementId: string): PElement =
 
 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. 
+  ## 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. 
+  ## 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
 
@@ -406,7 +406,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     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
@@ -414,7 +414,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     n = importedNode
     n.FOwnerDocument = doc
     n.FParentNode = nil
-    
+
     var tmpA: seq[PAttr] = n.attributes
     n.attributes = @[]
     # Import the Element node's attributes
@@ -426,7 +426,7 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     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
@@ -437,27 +437,27 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
     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:
+  if not isNil(n.childNodes) and 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:
+  if not isNil(n.childNodes) and n.childNodes.len() > 0:
     return n.childNodes[n.childNodes.len() - 1]
   else:
     return nil
-  
+
 proc localName*(n: PNode): string =
   ## Returns this nodes local name
 
@@ -465,15 +465,17 @@ proc localName*(n: PNode): string =
 
 proc namespaceURI*(n: PNode): string =
   ## Returns this nodes namespace URI
-  
+
   return n.FNamespaceURI
-  
-proc `namespaceURI=`*(n: PNode, value: string) = 
+
+proc `namespaceURI=`*(n: PNode, value: string) =
   n.FNamespaceURI = value
 
 proc nextSibling*(n: PNode): PNode =
   ## Returns the next sibling of this node
 
+  if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
+    return nil
   var nLow: int = low(n.FParentNode.childNodes)
   var nHigh: int = high(n.FParentNode.childNodes)
   for i in nLow..nHigh:
@@ -500,17 +502,19 @@ 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
 
+  if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes):
+    return nil
   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
 
@@ -519,13 +523,13 @@ proc `prefix=`*(n: PNode, value: string) =
   if illegalChars in value:
     raise newException(EInvalidCharacterErr, "Invalid character")
 
-  if n.FNamespaceURI == nil:
+  if isNil(n.FNamespaceURI):
     raise newException(ENamespaceErr, "namespaceURI cannot be nil")
   elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace":
-    raise newException(ENamespaceErr, 
+    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, 
+    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\"")
@@ -543,33 +547,33 @@ proc `prefix=`*(n: PNode, value: string) =
 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):
+  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 = 
+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:
@@ -586,7 +590,7 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
     # Import the childNodes
     var tmp: seq[PNode] = n.childNodes
     n.childNodes = @[]
-    if deep:
+    if deep and not isNil(tmp):
       for i in low(tmp.len())..high(tmp.len()):
         n.childNodes.add(cloneNode(tmp[i], deep))
     return newNode
@@ -597,34 +601,39 @@ proc cloneNode*(n: PNode, deep: bool): PNode =
     return newNode
 
 proc hasAttributes*(n: PNode): bool =
-  ## Returns whether this node (if it is an element) has any attributes. 
-  return n.attributes.len() > 0
+  ## Returns whether this node (if it is an element) has any attributes.
+  return not isNil(n.attributes) and n.attributes.len() > 0
 
-proc hasChildNodes*(n: PNode): bool = 
+proc hasChildNodes*(n: PNode): bool =
   ## Returns whether this node has any children.
-  return n.childNodes.len() > 0
+  return not isNil(n.childNodes) and 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.")
-    
+
+  if isNil(n.childNodes):
+    n.ChildNodes = @[]
+
   for i in low(n.childNodes)..high(n.childNodes):
     if n.childNodes[i] == refChild:
       n.childNodes.insert(newChild, i - 1)
-    return
+      return
+
+  n.ChildNodes.add(newChild)
 
 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. 
+  ## 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:
+  if isNil(s) or s == "":
     return True
   for i in items(s):
     if i != ' ':
@@ -635,18 +644,18 @@ 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:
+    if isNil(n.childNodes) or 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:
+
+      if isNil(curTextNode):
         curTextNode = n.childNodes[i]
       else:
         PText(curTextNode).data.add(PText(n.childNodes[i]).data)
@@ -656,35 +665,37 @@ proc normalize*(n: PNode) =
       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
-      
+  if not isNil(n.childNodes):
+    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
-  
+
+  if not isNil(n.childNodes):
+    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 =
@@ -693,22 +704,22 @@ proc getNamedItem*(NList: seq[PNode], name: string): PNode =
     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 = 
+
+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:
@@ -716,7 +727,7 @@ proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string)
   return nil
 
 proc item*(NList: seq[PNode], index: int): PNode =
-  ## Returns the ``index`` th item in the map. 
+  ## 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]
@@ -729,9 +740,9 @@ proc removeNamedItem*(NList: var seq[PNode], name: string): PNode =
       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):
@@ -739,7 +750,7 @@ proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName:
       result = NList[i]
       NList.delete(i)
       return result
-  
+
   raise newException(ENotFoundErr, "Node not found")
 
 proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
@@ -751,9 +762,9 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
       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:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -765,22 +776,22 @@ proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
         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 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:
+
+  if not isNil(arg.FOwnerElement):
     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:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -792,18 +803,18 @@ proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
         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 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:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -815,7 +826,7 @@ proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
         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):
@@ -823,13 +834,13 @@ proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
       # 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:
+
+  if not isNil(arg.FOwnerElement):
     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:
+  if isNil(item):
     NList.add(arg)
     return nil
   else:
@@ -841,8 +852,8 @@ proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
         break
     NList[index] = arg
     return item # Return the replaced node
-    
-# CharacterData - Decided to implement this, 
+
+# CharacterData - Decided to implement this,
 # Didn't add the procedures, because you can just edit .data
 
 # Attr
@@ -851,13 +862,13 @@ 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 = 
+
+proc ownerElement*(a: PAttr): PElement =
   ## Returns this Attributes owner element
 
   return a.FOwnerElement
@@ -873,27 +884,35 @@ proc tagName*(el: PElement): string =
 # Procedures
 proc getAttribute*(el: PElement, name: string): string =
   ## Retrieves an attribute value by ``name``
+  if isNil(el.attributes):
+    return nil
   var attribute = el.attributes.getNamedItem(name)
-  if attribute != nil:
+  if not isNil(attribute):
     return attribute.value
   else:
     return nil
 
 proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): string =
   ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
+  if isNil(el.attributes):
+    return nil
   var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
-  if attribute != nil:
+  if not isNil(attribute):
     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
+  if isNil(el.attributes):
+    return nil
   return el.attributes.getNamedItem(name)
 
 proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr =
   ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI``
+  if isNil(el.attributes):
+    return nil
   return el.attributes.getNamedItemNS(namespaceURI, localName)
 
 proc getElementsByTagName*(el: PElement, name: string): seq[PNode] =
@@ -909,103 +928,110 @@ proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: stri
   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
+  ## Returns ``true`` when an attribute with a given ``name`` is specified
+  ## on this element , ``false`` otherwise.
+  if isNil(el.attributes):
+    return false
+  return not isNil(el.attributes.getNamedItem(name))
 
 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
+  ## ``namespaceURI`` is specified on this element , ``false`` otherwise
+  if isNil(el.attributes):
+    return false
+  return not isNil(el.attributes.getNamedItemNS(namespaceURI, localName))
 
 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)
-      
+  if not isNil(el.attributes):
+    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)
-  
+  if not isNil(el.attributes):
+    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
-  
+  if not isNil(el.attributes):
+    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, 
+    raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
-        
-  if newAttr.FOwnerElement != nil:
-    raise newException(EInuseAttributeErr, 
+
+  if not isNil(newAttr.FOwnerElement):
+    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 
+  ## 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, 
+    raise newException(EWrongDocumentErr,
       "This node belongs to a different document, use importNode.")
-        
-  if newAttr.FOwnerElement != nil:
-    raise newException(EInuseAttributeErr, 
+
+  if not isNil(newAttr.FOwnerElement):
+    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 
+  ## 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 
+  ## 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`` 
+  ## 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
@@ -1013,19 +1039,19 @@ proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) =
   AttrNode.FSpecified = True
   AttrNode.value = value
 
-# Text  
+# Text
 proc splitData*(TextNode: PText, offset: int): PText =
-  ## Breaks this node into two nodes at the specified offset, 
+  ## 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:
+
+  if not isNil(TextNode.FParentNode) and not isNil(TextNode.FParentNode.childNodes):
     for i in low(TextNode.FParentNode.childNodes)..high(TextNode.FParentNode.childNodes):
       if TextNode.FParentNode.childNodes[i] == TextNode:
         var newNode: PText = TextNode.FOwnerDocument.createTextNode(right)
@@ -1042,10 +1068,10 @@ proc target*(PI: PProcessingInstruction): string =
 
   return PI.FTarget
 
-    
+
 # --Other stuff--
 # Writer
-proc addEscaped(s: string): string = 
+proc addEscaped(s: string): string =
   result = ""
   for c in items(s):
     case c
@@ -1057,10 +1083,11 @@ proc addEscaped(s: string): string =
 
 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:
+  if not isNil(n.attributes):
+    for i in items(n.attributes):
+      result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
+
+  if isNil(n.childNodes) or n.childNodes.len() == 0:
     result.add("/>") # No idea why this doesn't need a \n :O
   else:
     # End the beginning of this tag