ooRexx logo xmlDOM.cls
/**
  **********************************************************************

  Copyright (c) 2011-2015 Ruurd J. Idenburg. All rights reserved.                                            

  This program and the accompanying materials are made available under
  the terms of the Common Public License v1.0 which accompanies this
  distribution. A copy is also available at the following address:
 
  http://www.opensource.org/licenses/cpl1.0.php                        

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:                                                              

  Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.        

  Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.  
 
  Neither the name or trademarks of Ruurd J. Idenburg nor the names
  of any  contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.
 
  DISCLAIMER                                                            

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
  **********************************************************************
*/

/* CLASS: xmlUtil */
/**
  xmlDom basic types and other useful things
*/


/**
  XML name valid characters. For (oo)Rexx, which only supports ASCII at the moment the valid range is alpha (lower and
  upper) and digits, plus '.', '-', '_' and' :' (this last one for qualified names (i.e. namespace:tag)).
  @return valid - A string containing the valid characters for a XML name
*/

::routine xmlValidName public
valid = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_:'
return valid
/**
  Generates a "under construction" message for things not yet operational.
  @param name - The name of the thing the message is about
  @return message - The message text
*/

::routine isUnderConstruction public
  use strict arg name
  message = name "is currently under construction!"
return message
/**
  xmlDomString is a subclass of String, for the time being without any modifications.
*/
 
::class xmlDomString public subclass string
/**
  xmlDomTimeStamp represents a number of milliseconds
*/

::class xmlDomTimeStamp public subclass string
/**
  xmlDomUserData represents a reference to application data.
*/

::class xmlDomUserData public
/**
  xmlDomObject represents an object reference.
*/

::class xmlDomObject public

/**
  domExceptions and their codes.

  DOM operations only raise exceptions in "exceptional" circumstances, i.e., when an operation is impossible to perform
  (either for logical reasons, because data is lost, or because the implementation has become unstable). In general, DOM
  methods return specific error values in ordinary processing situations, such as out-of-bound errors when using
  NodeList.

  Implementations should raise other exceptions under other circumstances. For example, implementations should raise an
  implementation-dependent exception if a <b>.nil</b> argument is passed when null was not expected.

  Some languages and object systems do not support the concept of exceptions. For such systems, error conditions may be
  indicated using native error reporting mechanisms. For some bindings, for example, methods may return error codes
  similar to those listed in the corresponding method descriptions.

*/

::class domException public
/**
  INDEX_SIZE_ERR - If index or size is negative, or greater than the allowed
  value.
*/

::method index_size_err class
return 1
/**
  DOMSTRING_SIZE_ERR - If the specified range of text does not fit into a
  DOMString.
*/

::method domstring_size_err class
return 2
/**
  HIERARCHY_REQUEST_ERR - If any Node is inserted somewhere it doesn't belong.
*/

::method hierarchy_request_err class
return 3
/**
  WRONG_DOCUMENT_ERR - If a Node is used in a different document than the one
  that created it (that doesn't support it).
*/

::method wrong_document_err class
return 4
/**
  INVALID_CHARACTER_ERR - If an invalid or illegal character is specified, such
  as in an XML name.
*/

::method invalid_character_err class
return 5
/**
  NO_DATA_ALLOWED_ERR - If data is specified for a Node which does not support
  data
*/

::method no_data_allowed_err class
return 6
/**
 NO_MODIFICATION_ALLOWED_ERR - If an attempt is made to modify an object where
 modifications are not allowed.
*/

::method no_modification_allowed_err class
return 7
/**
  NOT_FOUND_ERR - If an attempt is made to reference a Node in a context where
  it does not exist.
*/

::method not_found_err class
return 8
/**
  NOT_SUPPORTED_ERR - If the implementation does not support the requested type of object or operation.
*/

::method not_supported_err class
return 9
/**
  INUSE_method_ERR - If an attempt is made to add an method that is already in use elsewhere.
*/

::method inuse_method_err class
return 10
/**
  INVALID_STATE_ERR - If an attempt is made to use an object that is not, or is no longer, usable.
*/

::method invalid_state_err class
return 11
/**
  SYNTAX_ERR - If an invalid or illegal string is specified.
*/

::method syntax_err class
return 12
/**
  INVALID_MODIFICATION_ERR - If an attempt is made to modify the type of the underlying object.
*/

::method invalid_modification_err class
return 13
/**
  NAMESPACE_ERR - If an attempt is made to create or change an object in a way which is incorrect with regard to
  namespaces.
*/

::method namespace_err class
return 14
/**
 INVALID_ACCESS_ERR - If a parameter or an operation is not supported by the underlying object.
*/

::method invalid_access_err class
return 15
/**
  VALIDATION_ERR - If a call to a method such as insertBefore or removeChild would make the Node invalid with respect to
  "partial validity", this exception would be raised and the operation would not be done. This code is used in [DOM
  Level 3 Validation]. Refer to this specification for further information.
*/

::method validation_err class
return 16
/**
  TYPE_MISMATCH_ERR - If the type of an object is incompatible with the expected type of the parameter associated to the
  object.
*/

::method type_mismatch_err class
return 17
/* :end */

/* CLASS: xmlNodeList */
/**
  The xmlNodeList interface provides the abstraction of an ordered collection of xmlNodes, without defining or
  constraining how this collection is implemented. xmlNodeList objects in the DOM are live.
 
  For this ooRexx implementation a xmlNodeLIst is defined as a resticted array, whose members must be an instance of
  xmLNode.
 
  The items in the xmlNodeList are accessible via an integral index, starting from 0.
*/

::class xmlNodeList public
/**
  Provide the number of xmlNodes in the xmlNodeList.
  @return count - The number of items (xmlNodes) in the xmlNodeList.
*/

::method length
return self~nodeList~items
/**
  The nodeList array can only be set by the xmlNodeList self
*/

::attribute nodeList get
::attribute nodeList set
/**
  xmlNodeList instance constructor.
 
  As the creation of the xmlNodeList is left to the implementer, for ooRexx the constructor expects to get the
  representation of the complete xmlNodeList as an ooRexx array.
  @param nodeList=(.array~new) - an ooRexx array of xmlNodes.
*/

::method init
  use strict arg nodeList=(.array~new)
  if \nodeList~isA(.array)
    then raise syntax 93.914 array (nodeList, .Array)
  self~nodeList = nodeList
exit
/**
  Retrieve the xmlNode at index position.
  @param index - The index (base 0) of the wanted Node.
  @return node - The xmlNode at the index in the xmlNodeList, or <b>.nil</b> if not a valid index.
*/

::method item
  use strict arg index
  node = .nil
  if index>=0
    then node = self~nodeList[index+1]
return node
/**
  Add a xmlNode to the xmlNodeList.
  @param node - The xmlNode to be added to the xmlNodeList.
  @raises user domException - HIERARCHY_REQUEST_ERR
 
*/

::method append
  use strict arg node
  if \node~isA(.xmlNode)
    then raise syntax 88.914 array (node, .xmlNode)
  self~nodeList~append(node)
return
/**
  Removes a xmlNode from the xmlNodeList.
  @param node - The xmlNode to be removed.
  @return node - The removed xmlNode or <b>.nil</b>, if not present.

  Note: On hold for possible future use
*/

::method remove private protected
  use strict arg node
  node = self~nodelist~remove(node)
return node
/**
  @return array - A native ooRexx array representing the xmlNodeList.
*/

::method toOorexxArray
return self~nodeList
/**
  Sets the xmlNodeList from a native ooRexx array.
  @param nodeArray - the native ooRexx array to replace an existing xmlNodeList.
*/

::method fromOorexxArray
  use strict arg nodeArray
  do i=1 to nodeArray~items
    if \nodeArray[i]~isA(.xmlNode)
      then raise syntax 88.914 array (nodeArray[i], .xmlNode)
  end
  self~nodeList = nodeArray
return
/* :end */

/* CLASS: xmlNode */
/**
  The <b>xmlNode</b> class is the base class for real nodetypes such as <i>elementNode</i> and <i>textNode</i>.
 
  Methods that make no sense or that are just plain impossible to implement in this base class, because the
  implementation is only possible in a subclass will raise syntax error 93.963 - Unsupported or not implented method.
 
  Methods that have a default value (e.g. .nil) will be intialized to that default value and need to be overridden in
  the appropiate subclass.
*/

::class xmlNode public
/**
  A convenience constant to implement <i>null</i>
*/

::constant null .nil
/**
  Constants that define the type of nodes that are possible.
*/

::constant elementNode 1
::constant attributeNode 2
::constant textNode 3
::constant cdataSectionNode 4
::constant entityReferenceNode 5
::constant entityNode 6
::constant processingInstructionNode 7
::constant commentNode 8
::constant documentNode 9
::constant documentTypeNode 10
::constant documentFragmentNode 11
::constant notationNode 12
/**
  <b>instances</b> is a private class attribute (property) that is used to
  keep track of the node instances being created and to provide a unique
  identifier for each node.
*/

::attribute instances class private
/**
  Gets a unique identifier for each (subclass) node being created.
  @return number - unique identifier for the new xmlNode
 
  The use of <u>use strict arg</u> makes sure there are no parameters.
*/

::method getId class
  use strict arg
  if instances~dataType('U') then instances = 0
  instances += 1
return instances
/**
  The unique node identification
*/

::attribute id private
/**
  A <b>NamedNodeMap</b> representing the attributes in an <b>elementNode</b>.
*/
 
::attribute attributes get
::attribute attributes set
/**
  The absolute base URI of this node or null if the implementation was not able to obtain an absolute URI.
*/

::attribute baseURI get
::attribute baseURI set
/**
  A <b>nodeList</b> containing the childNodes of a node. If there are no children, this is a NodeList containing no
  nodes.
*/

::attribute childNodes get
::attribute childNodes set
/**
  The local part of the qualified name of this node.
  <br>
  For nodes of any type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with a DOM Level 1 method, such as
  xmlDocument~createElement, this is always <b>.nil</b>.
*/

::attribute localName get
::attribute localName set
/**
  The namespace URI of this node, or null if it is unspecified. This is not a computed value that is the result of a
  namespace lookup based on an examination of the namespace declarations in scope. It is merely the namespace URI given
  at creation time.
  <br>
  For nodes of any type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with a DOM Level 1 method, such as
  xmlDocument~createElement, this is always <b>.nil</b>.
*/

::attribute namespaceURI get
::attribute namespaceURI set
/**

*/

::attribute nextSibling
/**
  The following nodeName values are valid for a particular type of node:
  <ul>
  <li><b>Attr</b> - xmlAttr~name (i.e. the name part of the name-value attribute pair).</li>
  <li><b>CDATASection</b> - literal "#cdata-section".</li>
  <li><b>Comment</b> - literal "#comment".</li>
  <li><b>Document</b> - literal "#document".</li>
  <li><b>DocumentFragment</b> - literal "#document-fragment".</li>
  <li><b>DocumentType</b> - xmlDocumentType~name.</li>
  <li><b>Element</b> - xmlElement~tagName.</li>
  <li><b>Entity</b> - xmlEntity name (e.g. ?????).</li>
  <li><b>EntityReference</b> - name of entity referenced (e.g. ?????).</li>
  <li><b>Notation</b> - notation name (e.g. ?????).</li>
  <li><b>ProcessingInstruction</b> - xmlProcessingInstruction~target.</li>
  <li><b>Text</b> - literal "#text".</li>
  </ul>
*/

::attribute nodeName get
::attribute nodeName set
/**
  An integer indicating which type of node this is. See above.
*/

::attribute nodeType get
::attribute nodeType set
/**
  The following nodeName values are valid for a particular type of node:
  <ul>
  <li><b>Attr</b> - xmlAttr~value (i.e. the value part of the name-value attribute pair).</li>
  <li><b>CDATASection</b> -  xmlCharacterData~data (i.e. the content of the CDATA Section).</li>
  <li><b>Comment</b> - xmlCharacterData.data (i.e. the content of the comment).</li>
  <li><b>Document</b> - <b>.nil</b></li>
  <li><b>DocumentFragment</b> - <b>.nil</b></li>
  <li><b>DocumentType</b> - <b>.nil</b></li>
  <li><b>Element</b> - <b>.nil</b></li>
  <li><b>Entity</b> - <b>.nil</b></li>
  <li><b>EntityReference</b> - <b>.nil</b></li>
  <li><b>Notation</b> - <b>.nil</b></li>
  <li><b>ProcessingInstruction</b> - xmlProcessingInstruction~data</li>
  <li><b>Text</b> - xmlCharacter~data (i.e. the content of the text node).</li>
  </ul>
*/

::attribute nodeValue get
::attribute nodeValue set
/**
  The xmlDocument object associated with this node. This is also the xmlDocument object used to create new nodes. When
  this node is a xmlDocument or a xmlDocumentType which is not used with any xmlDocument yet, this is <b>.nil</b>.
*/

::attribute ownerDocument get
::attribute ownerDocument set
/**
  The parent of this node. All nodes, except xmlAttr, xmlDocument, xmlDocumentFragment, xmlEntity, and xmlNotation may
  have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from
  the tree, this is <b>.nil</b>.
*/

::attribute parentNode get
::attribute parentNode set
/**
  The namespace prefix of this node, or null if it is unspecified. When it is defined to be <b>.nil</b>, setting it has
  no effect, including if the node is read-only.
 
  Note that setting this attribute, when permitted, changes the nodeName attribute, which holds the qualified name, as
  well as the tagName and name attributes of the xmlElement and xmlAttr interfaces, when applicable.
 
  Setting the prefix to null makes it unspecified, setting it to an empty string is implementation dependent. Note also
  that changing the prefix of an attribute that is known to have a default value, does not make a new attribute with the
  default value and the original prefix appear, since the namespaceURI and localName do not change.
 
  For nodes of any type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with a DOM Level 1 method, such as
  createElement from the xmlDocument interface, this is always <b>.nil</b>.
*/

::attribute prefix get
::attribute prefix set
/**

*/

::attribute previousSibling
/**
  This attribute returns the text content of this node and its descendants. When it is defined to be .nil, setting it
  has no effect. On setting, any possible children this node may have are removed and, if it the new string is not empty
  or null, replaced by a single xmlText node containing the string this attribute is set to.
*/

::method textContent
  raise syntax 93.960
exit
::attribute textContent set
/**
  Method to initially set read-only attributes
  @param attrName - the name of the Attribute.
  @param attrValue - the value of the attribute.
  @raises syntax 93.900 - Attribute value cannot be set twice
 
  If the attribute has been set already a syntx error will be raised
*/

::method setReadOnly
--trace i
  use strict arg attrName,attrValue
  if self~send(attrName)<>attrName~upper
    then raise syntax 93.900 array ("Attribute:" attrName "is read only, once set")
    else self~send(attrName'=',attrValue)
return
/**
  The instance constructor
*/

::method init
  self~id = self~class~getId
  self~attributes = .nil
  self~baseURI = .nil
  self~childNodes = .xmlNodeList~new(.array~new)
  self~localName = .nil
  self~namespaceURI = .nil
  self~nodeName = ''
  self~nodeValue = ''
  self~nodeType = .nil
  self~ownerDocument = .nil
  self~parentNode = .nil
  self~prefix = .nil
return
/**
  Adds the node <b>child</b> to the end of the list of children of this node. If the <b>child</b> is already in the
  tree, it is first removed.
  @param child - the node to be appended.
  @return node - the node appended
 
  If <b>child</b> is a xmlDocumentFragment object, the entire contents of the document fragment are moved into the child
  list of this node.
*/

::method appendChild
  use strict arg child
  appended = .nil
  if (child<>.nil) then do
    child~parentNode = self
    self~childNodes~append(child)
    appended = child
  end
return appended
/**
  @return nodes - A xmlNodeList that contains all the children of this node.
 
  If there are no children, then this is a xmlNodeList containing no nodes.
*/

::method children
  use strict arg
return self~childNodes
/**
  Returns a duplicate of this node, i.e., serves as a generic copy constructor for nodes. The duplicate node has no
  parent (parentNode is <b>.nil</b>) and no user data. User data associated to the imported node is not carried over.
  However, if any UserDataHandlers has been specified along with the associated data these handlers will be called with
  the appropriate parameters before this method returns.
  @param deep=.false - optional <b>.true</b> or <b>.false</b> (= default)
  @return node - the duplicated node

  If <i> deep</i> is <b>.true</b>, recursively clone the subtree under the specified node; if <b>.false</b>, clone only
  the node itself (and its attributes, if it is an xmlElement).
  Cloning an xmlElement copies all attributes and their values, including those generated by the XML processor to
  represent defaulted attributes, but this method does not copy any children it contains unless it is a <i>deep</i>
  clone. This includes text contained in the xmlElement since the text is contained in a child xmlText node.
  <br>
  Cloning an xmlAttr directly, as opposed to be cloned as part of an xmlElement cloning operation, returns a specified
  attribute (specified is true). Cloning an xmlAttr always clones its children, since they represent its value, no
  matter whether this is a <i>deep</i> clone or not.
  <br>
  Cloning an xmlEntityReference automatically constructs its subtree if a corresponding xmlEntity is available, no
  matter whether this is a <i>deep</i> clone or not.
  <br>
  Cloning any other type of node simply returns a copy of this node.
  <br>
  Note that cloning an immutable subtree results in a mutable copy, but the children of an xmlEntityReference clone are
  readonly. In addition, clones of unspecified xmlAttr nodes are specified. And, cloning xmlDocument, xmlDocumentType,
  xmlEntity, and xmlNotation nodes is implementation dependent.
*/

::method cloneNode
  use strict arg deep=.false
  clone = self~copy
  clone~parentNode = .nil
  if deep = .true then do
     raise syntax 93.963
  end
return clone
/**
  Compares the reference node, i.e. the node on which this method is being called, with a node, i.e. the one passed as a
  parameter, with regard to their position in the document and according to the document order.
  @param other - The node to compare with
  @return number - Position relatively to the reference node
*/

::method compareDocumentPosition
  use strict arg other
  raise syntax 93.963
exit
/**
  @return child - The first child of this node

  If there is no such node, this returns <b>.nil</b>
*/

::method firstChild
  use strict arg
  child = .nil
  if (self~childNodes~length>0) then child = self~Childnodes~item(0)
return child
/**
  @param feature - The name of the feature requested
  @param version - The version number of the feature to test
  @return domObject - An object which implements the specialized APIs or <b>.nil</b>
   
  This method returns a specialized object which implements the specialized APIs of the specified feature and version,
  as specified in DOM Features. The specialized object may also be obtained by using binding-specific casting methods
  but is not necessarily expected to.
  <br>
  This method also allows the implementation to provide specialized objects which do not support the xmlNode interface.
*/

::method getFeature
  use strict arg feature, version
  raise syntax 93.963
exit
/**
  Retrieves the object associated to a key on a this node. The object must first have been set to this node by calling
  <b>setUserData</b> with the same key.
  @param key - The key the object is associated to.
  @return userdata - the DOMUserData associated, or <b>.nil</b> if there was none
*/

::method getUserData
  use strict arg key
  raise syntax 93.963
exit
/**
  @return boolean - <b>.true</b> if this node has any attributes, <b>.false</b> otherwise
*/

::method hasAttributes
  use strict arg
return .false
/**
  @return boolean - <b>.true</b> if this node has any children, <b>.false</b> otherwise
*/

::method hasChildNodes
  use strict arg
  hasChildren = .false
  if (self~children~items>0) then hasChildren = .true
return hasChildren
/**
  Inserts the node <i>child</i> before the existing child node <i>where</i>.
  @param child - the node to be inserted
  @param where - the node before which the new node needs to be inserted.
  @return node - the node being inserted

  If <i>where</i> is .nil, insert <i>child</i> at the end of the list of children.
  <br>
  If <i>child</i> is a xmlDocumentFragment object, all of its children are inserted, in the same order, before
  <i>where</i>.
  <br>
  If the <i>child</i> is already in the tree, it is first removed.
  <br>
  Note: Inserting a node before itself is implementation dependent.
*/

::method insertBefore
  use strict arg child, where
  if (where==.nil) then do
     self~childNodes~append(child)
  end
  -- find the where node
  else do
    newList = .xmlNodeList~new
    do i=0 to self~childNodes~length-1
     if self~childNodes~item(i) == where then newList~append(child)
     newList~append(self~childNodes~item(i))
    end
    self~childNodes = newList
  end
  child~parentNode = self
return child
/**
  This method checks if the specified <i>namespaceURI</i> is the default namespace or not.
  @param uri - The <i>namespaceURI</i> to look for
  @return boolean - <b>.true</b> if default, <b>.false</b> otherwise
*/

::method isDefaultNamespace
  use strict arg uri
  raise syntax 93.963
exit  
/**
  Tests whether two nodes are equal.
  @param other - The node to compare equality with
  @return boolean - <b>.true</b> if equal, <b>.false</b> otherwise

 
  This method tests for equality of nodes, not sameness (i.e., whether the two nodes are references to the same object)
  which can be tested with xmlNode~isSameNode. All nodes that are the same will also be equal, though the reverse may
  not be true.
  <br>
  Two nodes are equal if and only if the following conditions are satisfied:
  <ul>
  <li>The two nodes are of the same type.</li>
  <li>The following string attributes are equal: nodeName, localName, namespaceURI, prefix, nodeValue. This is: they are
  both null, or they have the same length and are character for character identical.</li>
  <li>The attributes xmlNamedNodeMaps are equal. This is: they are both null, or they have the same length and for each
  node that exists in one map there is a node that exists in the other map and is equal, although not necessarily at the
  same index.<li>
  <li>The childNodes xmlNodeLists are equal. This is: they are both null, or they have the same length and contain equal
  nodes at the same index.
  <br>Note that normalization can affect equality; to avoid this, nodes should be normalized before being compared.</li>
  </ul>
  For two xmlDocumentType nodes to be equal, the following conditions must also be satisfied:
  <ul>
  <li>The following string attributes are equal: publicId, systemId, internalSubset.</li>
  <li>The entities xmlNamedNodeMaps are equal.</li>
  <li>The notations NamedNodeMaps are equal.</li>
  </ul>
  On the other hand, the following do not affect equality: the ownerDocument, baseURI, and parentNode attributes, the
  specified attribute for xmlAttr nodes, the schemaTypeInfo attribute for xmlAttr and xmlElement nodes, the
  xmlText~isElementContentWhitespace attribute for xmlText nodes, as well as any user data or event listeners registered
  on the nodes.
*/

::method isEqualNode
  use strict arg other
  raise syntax 93.963
exit
/**
  Tests whether this node is the same node as the given one.
  @param other - The node to test against.
  @return boolean - <b>.true</b> if the nodes are the same, <b>.false</b> otherwise.

  This method provides a way to determine whether two xmlNode references returned by the implementation reference the
  same object. When two xmlNode references are references to the same object, even if through a proxy, the references
  may be used completely interchangeably, such that all attributes have the same values and calling the same DOM method
  on either reference always has exactly the same effect.
*/

::method isSameNode
  use strict arg other
  raise syntax 93.963
exit
/**
  Tests whether the DOM implementation implements a specific feature and that feature is supported by this node, as
  specified in DOM Features.
  @param feature - The name of the feature to test.
  @param version - This is the version number of the feature to test.
  @return boolean - <b>.true</b> if supported on this node, <b>.false</b> otherwise.
*/
 
::method isSupported
  use strict arg feature, version
return .false
/**
  @return aNode - The last child of this node.
 
  If there is no such node, this returns <b>.nil</b>.
*/

::method lastChild
  use strict arg
  child = .nil
  if (self~children~items>0) then child = self~children[self~children~items]
return child
/**
  Look up the <i>namespaceURI</i> associated to the given prefix, starting from this node.
  <br>
  See Namespace URI Lookup for details on the algorithm used by this method.
  @param prefix - The prefix to look for.
  @return aString - The associated <i>namespaceURI</i> or <b>.nil</b> if none is found.
 
  If <i>prefix</i> is null, the method will return the default <i>namespaceURI</i> if any.
*/

::method lookupNamespaceURI
  use strict arg prefix
  raise syntax 93.963
exit
/**
  Look up the prefix associated to the given <i>namespaceURI</i>, starting from this node.
  @param uri - A string specifying the <i>namespaceURI</i> to look for
  @return aString - An associated namespace prefix or <b>.nil</b> if none is found
 
  The default namespace declarations are ignored by this method. See Namespace Prefix Lookup for details on the
  algorithm used by this method.
  <br>
  If more than one prefix are associated to the namespace prefix, the returned namespace prefix is implementation
  dependent.
*/

::method lookupPrefix
  use strict arg uri
  raise syntax 93.963
exit
/**
  Finds the next sibling.
  @return aNode - The node immediately following this node.
 
  If there is no such node, this returns <b>.nil</b>.
*/

/*::method nextSibling
  use strict arg
  sibling = .nil
  if (self~parentNode<>.nil) then do
    siblings = self~parentNode~childNodes
    do label next i=1 to siblings~items
      if (siblings[i]==self) then do
        if (i<siblings~items) then do
          sibling = siblings[i-1]
          leave next
        end
      end
    end
  end
return sibling
*/

/**
  Puts all xmlText nodes in the full depth of the sub-tree underneath this xmlNode, including attribute nodes, into a
  "normal" form where only structure (e.g., elements, comments, processing instructions, CDATA sections, and entity
  references) separates xmlText nodes, i.e., there are neither adjacent xmlText nodes nor empty xmlText nodes. This can
  be used to ensure that the DOM view of a document is the same as if it were saved and re-loaded, and is useful when
  operations (such as XPointer [XPointer] lookups) that depend on a particular document tree structure are to be used.
  <br>
  If the parameter "normalize-characters" of the DOMConfiguration object attached to the xmlNode~ownerDocument is
  <b>.true</b>, this method will also fully normalize the characters of the xmlText nodes.
  <br>
  Note: In cases where the document contains CDATASections, the normalize operation alone may not be sufficient, since
  XPointers do not differentiate between xmlText nodes and xmlCDATASection nodes.
*/

::method normalize
  use strict arg
  raise syntax 93.963
exit
/**
  Finds the preceding node.
  @return aNode - The node immediately preceding this node.
 
  If there is no such node, this returns <b>.nil</b>
*/

/**
::method previousSibling
  use strict arg
  sibling = .nil
  if (self~parentNode<>.nil) then do
    siblings = self~parentNode~childNodes
    do labl prev i=1 to siblings~items
      if (siblings[i]==self) then do
        if (i>1) then do
          sibling = siblings[i-1]
          leave prev
        end
      end
    end
  end
return sibling
*/

/**
  Removes the child node indicated by <i>aNode</i> from the list of children, and returns it.
  @param child - The node being removed.
  @return aNode - The node removed.
*/

::method removeChild
  use strict arg child
  removed = .nil
  if (child<>.nil) then do
    -- find the reference node
    do i=1 to self~children~items
      if (children[i]==child) then leave
    end
    removed = self~children~remove(i)
    removed~parentNode = .nil
  end
return removed
/**
  Replaces the child node <i>old</i> with <i>new</i> in the list of children, and returns the <i>old</i> child node.
  @param new - The new node to put in the child list.
  @param old - The node being replaced in the list.
  @return aNode - The node replaced.
 
  If <i>new</i> is a xmlDocumentFragment object, <i>old</i> is replaced by all of the
  xmlDocumentFragment children, which are inserted in the same order. If the <i>new</i>
  is already in the tree, it is first removed.
  <br>
  Note: Replacing a node with itself is implementation dependent.
*/

::method replaceChild
  use strict arg new, old
  replaced = .nil
  if (old<>.nil) then do
  -- find the reference node
    do i=1 to self~children~items
       if (self~children[i]==old) then leave
    end
    replaced = self~children[i]
    self~children[i] = new
    self~children[i]~parentNode = self
    replaced~parentNode = .nil
  end
return replaced
/**
  Associate an object to a key on this node. The object can later be retrieved from this node by invoking the
  getUserData method with the same key.
  @param key - The key to associate the object to.
  @param data - A DOMUserData object to associate to the key, or null to remove any existing association to that key.
  @param handler - An UserDataHandler, a handler to associate to that key, or null.
  @return userData - The DOMUserData previously associated to the given key on this node, or null if there was none.
*/

::method setUserData
  use strict arg key, data, handler
  raise syntax 93.963
exit
/**
  Convenience method to walk thru a (sub)nodes tree
  @param nodes - The sibling xmlNodeList to start the walk.
  @param name - The name of the tag being searched for.
  @param nl - The xmlNodeList containing the selected result nodes.
*/

::method treeWalk private
  use strict arg nodes, name, nl
  do n=0 to nodes~length-1
    if name<>'*' then do
      if nodes~item(n)~nodeName==name then nl~append(nodes~item(n))
    end
    else do
      nl~append(nodes~item(n))
    end
    if nodes~item(n)~childNodes~length>0 then do
      self~treewalk(nodes~item(n)~childNodes, name, nl)
    end
  end
return
/* :end */

/* CLASS: xmlNamedNodeMap */
/**
  Objects implementing the xmlNamedNodeMap interface are used to represent collections of nodes that can be accessed by
  name. Note that xmlNamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular
  order. Objects contained in an object implementing xmlNamedNodeMap may also be accessed by an ordinal index, but this
  is simply to allow convenient enumeration of the contents of a xmlNamedNodeMap, and does not imply that the DOM
  specifies an order to these xmlNodes. xmlNamedNodeMap objects in the DOM are live.
*/

::class xmlNamedNodeMap public
/**
  A private array with directory entries, that have a (possibly qualified) name as index and xmlNodes as items.
*/

::attribute namedMap private
/**
  xmlNamedNodeMap instance constructor.
*/

::method init
  self~namedMap = .directory~new
  self~init:super
exit
/**
  The number of xmlNodes in this map.
  @return - The number of nodes in the xmlNamedNodeMap.
*/

::method length
return self~namedMap~items
/**
  Retrieves a node specified by name.
  @param name - The nodeName of a node to retrieve.
  @return node - The xmlNode with the specified name, or <b>.nil</b>.
*/

::method getNamedItem
  use strict arg name
  node = .nil
  if name~isA(.string) then do
    node = self~namedMap[name]
  end
return node
/**
  Retrieves a node specified by local name and namespace URI.
  @param namespaceURI - The namespace URI of the node to retrieve.
  @param localName - The local name of the node to retrieve.
  @return node - A xmlNode(of any type) with the specified local name and namespace URI, or null.
 
  Per [XML Namespaces], applications must use the value <b>.nil,</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.
*/

::method getNamedItemNS
  use strict arg namespaceURI, localName
  node = .nil
  if (localName)~isA(.string) then do
    node = self~namedMap[localName]
    if namespaceURI<>.nil then do
      if node~namespaceURI<>namespaceURI then node = .nil
    end
  end
return node
/**
  Returns the indexth item in the map. If index is greater than or equal to the number of nodes in this map, this
  returns <b>.nil</b>.
  @param index - Index into this map.
  @return node - The node at the indexth position in the map, or <B>.nil</b> if not a valid index.
*/

::method item
  use strict arg index
  node = .nil
  if index>=0 then do  
    node = self~namedMap~allItems[index+1]
  end  
return node
/**
  Removes a node specified by name.
  @param name - The nodeName of the node to remove.
  @return node - The node removed from this map if a node with such a name exists.
  @raises user domException - NOT_FOUND_ERR
 
  When this map contains the attributes attached to an element, if the removed attribute is known to have a default
  value, an attribute immediately appears containing the default value as well as the corresponding namespace URI,
  local name, and prefix when applicable.
 
  This ooRexx implementation does not process a DTD, thus has no way to determine if a default value has to be provided.
  The node is only removed, no additional checks for default value are made.
*/

::method removeNamedItem
  use strict arg name
  node = .nil
  if name~isA(.string) then do
    node = self~namedMap~remove(name)
  end
  if node==.nil then raise user domException description(.domException~not_found_err)
return node
/**
  Removes a node specified by local name and namespace URI. A removed attribute may be known to have a default value
  when this map contains the attributes attached to an element, as returned by the attributes attribute of the xmlNode
  interface. If so, an attribute immediately appears containing the default value as well as the corresponding
  namespace URI, local name, and prefix when applicable.
  @param namespaceURI=.nil - The namespace URI of the node to remove.
  @param localName - The local name of the node to remove.
  @return node - The node removed from this map if a node with such a name exists.
  @raises user domException - NOT_FOUND_ERR
 
  Per [XML Namespaces], applications must use the value <b>.nil</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.
  @see #removeNamedItem for behaviour concerning default values.
*/

::method removeNamedItemNS
  use strict arg namespaceURI=.nil, localName
  node = .nil
  if (localName)~isA(.string) then do
    if node~namespaceURI==namespaceURI
      then node = self~namedMap~remove(localName)
      else node = .nil
  end
return node
/**
  Adds a xmlNode using its <i>nodeName</i> attribute. If a node with that name is already present in this map, it is
  replaced by the new one. Replacing a node by itself has no effect.
  @param arg - A xmlNode to store in this map, accessible using its <i>nodeName</i> attribute.
  @return node - The replaced xmlNode or <b>.nil</b> if no replacement.
  @raises user domException - WRONG_DOCUMENT_ERR
  @raises user domException - INUSE_ATTRIBUTE_ERR
*/
 
::method setNamedItem
  use strict arg arg
  --if arg~ownerDocument<>self~ownerDocument
  --  then raise user domException description (.domException~wrong_document_err)
  if arg~nodeType==.xmlNode~attributeNode then do
    if arg~ownerElement<>.nil
      then raise user domException description (.domException~inuse_attribute_err)
  end
  node = .nil
  if arg~isA(.xmlNode) then do
    name = arg~nodeName
    node = self~namedMap[name]
    self~namedMap[name] = arg
  end
return node
/**
  Adds a node using its namespaceURI and localName. If a node with that namespace URI and that local name is already
  present in this map, it is replaced by the new one. Replacing a node by itself has no effect.
  @param arg - A node to add to the map, subsequently accessible via <i>namespaceURI</i> and <i>localName</i>.
  @return node - The replaced xmlNode or <b>.nil</b> if no replacement.
  @raises user domException - WRONG_DOCUMENT_ERR
  @raises user domException - INUSE_ATTRIBUTE_ERR
*/

::method setNamedItemNS
  use strict arg arg
  if arg~ownerDocument<>self~ownerDocument
    then raise user domException description (.domException~wrong_document_err)
  if arg~nodeType==.attributeElement then do
    if arg~ownerElement<>.nil
      then raise user domException description (.domException~inuse_attribute_err)
  end
  node = .nil
  if arg~isA(.xmlNode) then do
    name = arg~localName
    node = self~namedMap[name]
    -- the following replaces also a node from a different namespace
    -- probably need a directory with name indexes and items of a directory with namespace indexes and node items
    if node~namespaceURI<>arg~namespaceURI
      then node = .nil
      else self~namedMap[name] = arg
  end
return node
/**
  Provides a xmlNamedNodeMap as a native ooRexx directory
  @return directory - A native ooRexx directory representing the NamedNodeMap.
*/

::method toOorexxDirectory
return self~namedMap
/**
  Sets a NamedNodeMap from a native ooRexx directory
  @param nodeDirectory - The native ooRexx directory to replace an existing xmlNamedNodeMap.
  @raises syntax 93.914 - Named item is not a proper xmlNode
*/

::method fromOorexxDirectory
  use strict arg nodeDirectory
  s = nodeDirectory~supplier
  do while s~available
    if \s~item~isA(.xmlNode) then do
      raise syntax 93.914 array (nodeDirectory, .xmlNode)
    end
    s~next
  end
  self~namedMap = nodeDirectory
return
/* :end */

/* CLASS: xmlAttr */
/**
  The xmlAttr interface represents an attribute in an xmlElement object. Typically the allowable values for the
  attribute are defined in a schema associated with the document.

  xmlAttr objects inherit the xmlNode interface, but since they are not actually child nodes of the element they
  describe, the DOM does not consider them part of the document tree. Thus, the xmlNode attributes <i>parentNode</i>,
  <i>previousSibling</i>, and <i>nextSibling</i> have a <b>.nil</b> value for xmlAttr objects. The DOM takes the view
  that attributes are properties of elements rather than having a separate identity from the elements they are
  associated with; this should make it more efficient to implement such features as default attributes associated with
  all elements of a given type. Furthermore, xmlAttr nodes may not be immediate children of a xmlDocumentFragment.
  However, they can be associated with xmlElement nodes contained within a xmlDocumentFragment. In short, users and
  implementors of the DOM need to be aware that xmlAttr nodes have some things in common with other objects inheriting
  the Node interface, but they also are quite distinct.

  The attribute's effective value is determined as follows: if this attribute has been explicitly assigned any value,
  that value is the attribute's effective value; otherwise, if there is a declaration for this attribute, and that
  declaration includes a default value, then that default value is the attribute's effective value; otherwise, the
  attribute does not exist on this element in the structure model until it has been explicitly added. Note that the
  xmlNode <i>nodeValue</i> attribute on the xmlAttr instance can also be used to retrieve the string version of the
  attribute's value(s).

  If the attribute was not explicitly given a value in the instance document but has a default value provided by the
  schema associated with the document, an attribute node will be created with specified set to false. Removing attribute
  nodes for which a default value is defined in the schema generates a new attribute node with the default value and
  specified set to false. If validation occurred while invoking the xmlDocument <i>normalizeDocument</i>, attribute
  nodes with specified equals to false are recomputed according to the default attribute values provided by the schema.
  If no default value is associate with this attribute in the schema, the attribute node is discarded.

  In XML, where the value of an attribute can contain entity references, the child nodes of the xmlAttr node may be
  either xmlText or xmlEntityReference nodes (when these are in use; see the description ofxml EntityReference for
  discussion).
*/


::class xmlAttr public subclass xmlNode
/**
  Returns whether this attribute is known to be of type ID (i.e. to contain an identifier for its owner element) or not.
  When it is and its value is unique, the ownerElement of this attribute can be retrieved using the  xmlDocument
  <i>getElementById</i> method. The implementation could use several ways to determine if an attribute node is known to
  contain an identifier:
  <ul>
  <li>If validation occurred using an XML Schema [XML Schema Part 1] while loading the document or while invoking the
  xmlDocument <i>normalizeDocument</i> method, the post-schema-validation infoset contributions (PSVI contributions)
  values are used to determine if this attribute is a schema-determined ID attribute using the schema-determined ID
  definition in [XPointer].</li>
  <li>If validation occurred using a DTD while loading the document or while invoking  the xmlDocument
  <i>normalizeDocument</i> method, the infoset [type definition] value is used to determine if this attribute is a
  DTD-determined ID attribute using the DTD-determined ID definition in [XPointer].</li>
  <li>From the use of the xmlElement <i>setIdAttribute</i>, <i>setIdAttributeNS</i>, or <i>setIdAttributeNode</i>, i.e.
  it is an user-determined ID attribute;</li>
  <li>Using mechanisms that are outside the scope of this specification, it is then an externally-determined ID
  attribute. This includes using schema languages different from XML schema and DTD.</li>

  If validation occurred while invoking the xmlDocument <i>normalizeDocument</i> method, all user-determined ID
  attributes are reset and all attribute nodes ID information are then reevaluated in accordance to the schema used. As
  a consequence, if the xmlAttr <i>schemaTypeInfo</i> attribute contains an ID type, <i>isId</i> will always return
  <b>.true</b>.
*/

::attribute isId get
::attribute isId set
/**
  Returns the name of this attribute. If the xmlNode <i>localName</i> is different from <b>.nil</b>, this attribute is a
  qualified name.
*/

::method name
return self~nodeName
/**
  The xmlElement node this attribute is attached to or <b>.nil</b> if this attribute is not in use.
*/

::attribute ownerElement get
::attribute ownerElement set
/**
  The type information associated with this attribute. While the type information contained in this attribute is
  guarantee to be correct after loading the document or invoking the xmlDocument <i>normalizeDocument</i> method,
  <i>schemaTypeInfo</i> may not be reliable if the node was moved.
*/

::attribute schemaTypeInfo get
::attribute schemaTypeInfo set
/**
  <b>.true</b> if this attribute was explicitly given a value in the instance document, false otherwise. If the
  application changed the value of this attribute node (even if it ends up having the same value as the default value)
  then it is set to <b>.true</b>. The implementation may handle attributes with default values from other schemas
  similarly but applications should use the xmlDocument <i>normalizeDocument</i> method to guarantee this information is
  up-to-date.
*/

::attribute specified get
::attribute specified set
/**
  On retrieval, the value of the attribute is returned as a string. Character and general entity references are replaced
  with their values. See also the method <i>getAttribute</i> on the xmlElement interface.

   On setting, this creates a xmlText node with the unparsed contents of the string, i.e. any characters that an XML
   processor would recognize as markup are instead treated as literal text. See also the xmlElement <i>setAttribute</i>
   method.
*/

::method value
return self~nodeValue
/**
  xmlAttr instance creation method
*/

::method init
  self~isId = .false
  self~ownerElement = .nil
  self~schemaTypeInfo = .nil  -- not supported/implemented
  self~specified = .false
  self~init:super
return
/* :end */

/* CLASS: xmlCharacterData */
/**
  The CharacterData interface extends xmlNode with a set of attributes and methods for accessing character data in the
  DOM. For clarity this set is defined here rather than on each object that uses these attributes and methods. No DOM
  objects correspond directly to CharacterData, though Text and others do inherit the interface from it. All offsets in
  this interface start from 0.

  The following DOES NOT APPLY FOR this ooRexx xmlDOM implementation. Characters are ASCII 8-bit.
  As explained in the DOMString interface, text strings in the DOM are represented in UTF-16, i.e. as a sequence of
  16-bit units. In the following, the term 16-bit units is used whenever necessary to indicate that indexing on
  CharacterData is done in 16-bit units.
*/

::class xmlCharacterData public mixinclass xmlNode
/**
  The data contained in a xmlNode
*/

::method data
return self~nodeValue
/**
*/

::method length
return self~data~length
/**
  xmlCharacterData instance constructor
  @param data - The character data
*/

::method init
  use strict arg data
  self~init:super
  if arg~isA(.string)
    then self~nodeValue = data
    else raise user domException description (.domException~invalid_modification_err)
exit
/**
  Append the string to the end of the character data. Upon success, <i>data</i> and <i>length</i> reflect the change.  
  @param arg - The data to be appended
*/

::method appendData
  use strict arg arg
  if arg~isA(.string)
    then self~data ||= arg
    else raise user domException description (.domException~invalid_modification_err)
return
/**
  Remove a range of characters from the node. Upon success, <i>data</i> and <i>length</i> reflect the change.
  @param offset - The index(base 0) where to start deleting data
  @param count - The number of characters to delete
*/

::method deleteData
  use strict arg offset, count
  self~data = self~data~delStr(offset+1,count)
return
/**
  Insert a string at the specified offset.
  @param offset - The index where to start inserting data
  @param string - The character data to insert
*/

::method insertData
  use strict arg offset, string
  if offset>self~data~length | count<0
    then raise user domException description (.domException~index_size_err)  
  self~data = self~data~substr(1,offset) || string || self~data~substr(offset+1)
return
/**
  Replace the characters starting at the specified offset with the specified string.
  @param offset - The offset from which to start replacing.
  @param count - The number of characters to replace.
  @param string - The string with which the range must be replaced.

  If the sum of offset and count exceeds length, then all data to the end of the data are replaced; (i.e., the effect is
  the same as a remove method call with the same range, followed by an append method invocation).
  */

::method replaceData
  use strict arg offset, count, string
  if offset<0 | offset>self~data~length | count<0
    then raise user domException description (.domException~index_size_err)
  self~data = self~data~overlay(string,offset+1,count)
return
/**
  Extracts a range of data from the node.
  @param offset - The index where to start retrieving data
  @param count - The number of characters to retrieve
  @return string - The specified substring. If the sum of offset and count exceeds the length, then all characters to
  the end of the data are returned.
*/

::method substringData
  use strict arg offset, count
  if offset<0 | offset>self~data~length | count<0
    then raise user domException description (.domException~index_size_error)
return self~data~substr(offset+1,count)
/* :end */

/* CLASS: xmlComment */
/**
  This interface inherits from xmlCharacterData and represents the content of a comment, i.e., all the characters
  between the starting '<!--' and ending '-->'. Note that this is the definition of a comment in XML, and, in practice,
  HTML, although some HTML tools may implement the full SGML comment structure.

  No lexical check is done on the content of a comment and it is therefore possible to have the character sequence "--"
  (double-hyphen) in the content, which is illegal in a comment per section 2.5 of [XML 1.0]. The presence of this
  character sequence must generate a fatal error during serialization.
*/

::class xmlComment public subclass xmlNode inherit xmlCharacterData
/**
  commentNode instance constructor.
  @param data - The string representing the XML comment
*/

::method init
  use strict arg data
  self~init:super(data)
return
/* :end */

/* CLASS: xmlText */
/**
  The XMLText interface inherits from XMLCharacterData and represents the textual content (termed character data in XML)
  of an xmlElement or xmlAttr. If there is no markup inside an element's content, the text is contained in a single
  object implementing the xmlText interface that is the only child of the element. If there is markup, it is parsed into
  the information items (elements, comments, etc.) and xmlText nodes that form the list of children of the element.

  When a document is first made available via the DOM, there is only one xmlText node for each block of text. Users may
  create adjacent xmlText nodes that represent the contents of a given element without any intervening markup, but
  should be aware that there is no way to represent the separations between these nodes in XML or HTML, so they will not
  (in general) persist between DOM editing sessions. The xmlNode <i>normalize</i> method merges any such adjacent
  xmlText objects into a single node for each block of text.

  No lexical check is done on the content of a xmlText node and, depending on its position in the document, some
  characters must be escaped during serialization using character references; e.g. the characters "<&" if the textual
  content is part of an element or of an attribute, the character sequence "]]>" when part of an element, the quotation
  mark character " or the apostrophe character ' when part of an attribute.
*/

::class xmlText public subclass xmlNode inherit xmlCharacterData
/**
  Returns whether this text node contains element content whitespace, often abusively called "ignorable whitespace". The
  text node is determined to contain whitespace in element content during the load of the document or if validation
  occurs while using the xmlDocument <i>normalizeDocument</i> method.
*/

::attribute isElementContentWhitespace get
::attribute isElementContentWhitespace set private
/**
  Returns all text of xmlText nodes logically-adjacent text nodes to this node, concatenated in document order.
 
  For instance, in the example below <i>wholeText</i> on the xmlText node that contains "bar" returns "barfoo", and also
  on the xmlText node that contains "foo" it returns "barfoo".
*/

::method wholeText
  siblings = self~parentNode~childNodes
  wholeText = ''
  do s=0 to siblings~length-1
    wholeText = wholeText || s~data' '
  end
return wholeText~strip('T')
/**
  xmlText instance constructor
  @param content - The text string representing the content
*/

::method init
  use strict arg content
  self~init:super(content)
  if self~class==.xmlText
    then do
      self~nodeType = .xmlNode~textNode
      self~nodeName = "#text"
    end
    else do
      self~nodeType = .xmlNode~CDATASectionNode
      self~nodeName = "#cdata-section"
    end
  --self~nodeValue = content
return
 
/**
  Replaces the text of the current node and all logically-adjacent text nodes with the specified text. All
  logically-adjacent text nodes are removed including the current node unless it was the recipient of the replacement
  text.
  This method returns the node which received the replacement text. The returned node is:
  <ul>
  <li><b>.nil</b>, when the replacement text is the empty string;</li>
  <li>the current node, except when the current node is read-only;</li>
  <li>a new xmlText node of the same type (xmlText or xmlCDATASection) as the current node inserted at the location of
  the replacement.</li>
  </ul>
  @param content - The content of the replacing xmlText node.
  @return text - The xmlText node created with the specified content.
 
  Assumption: Text and CDATASections are the only ones within Element nodes (and possibly within Attr nodes).
*/

::method replaceWholeText
  use strict arg content
  text = .nil
  if content<>'' then do
    text = self~class~new(content)
    nodeList = .xmlNodeList~new(.array~of(text))
    self~parentNode~childNodes = nodeList
  end
return text
/**
  Breaks this node into two nodes at the specified offset, keeping both in the tree as siblings. After being split, this
  node will contain all the content up to the offset point. A new node of the same type, which contains all the content
  at and after the offset point, is returned. If the original node had a parent node, the new node is inserted as the
  next sibling of the original node. When the offset is equal to the length of this node, the new node has no data.
  @param offset - The offset at which to split, starting from 0.
  @return text - The new node, of the same type as this node.
  @raises user domException - INDEX_SIZE_ERR
*/
   
 ::method splitText
  use strict arg offset
  if offset+1>self~data~length then do
    raise user domException additional (.domException~index_size_err) description ("Offset" offset "is larger than length" self~data~length "of text")
  end
  text = self~class~new(self~data~substr(offset+1))
  self~data = self~data~substr(1,offset)
  siblings = .array~new
  do c=0 to self~parentNode~childNodes-1
    child = self~parentNode~childNodes~item(c)
    siblings~append(child)
    if child==self then do
      siblings~append(text)
    end
  end
  children = .xmlNodelist~new(siblings)
  self~parentNode~childNodes = children
return text
/* :end */

/* CLASS: xmlCDATASection */
/**
  CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup.
  The only delimiter that is recognized in a CDATA section is the "]]>" string that ends the CDATA section. CDATA
  sections cannot be nested. Their primary purpose is for including material such as XML fragments, without needing to
  escape all the delimiters.

  The xmlCharacterData <i>data</i> attribute holds the text that is contained by the CDATA section. Note that this may
  contain characters that need to be escaped outside of CDATA sections and that, depending on the character encoding
  ("charset") chosen for serialization, it may be impossible to write out some characters as part of a CDATA section.

  The CDATASection interface inherits from the xmlCharacterData interface through the xmlText interface. Adjacent
  xmlCDATASection nodes are not merged by use of the <i>normalize</i> method of the xmlNode interface.

  No lexical check is done on the content of a CDATA section and it is therefore possible to have the character sequence
  "]]>" in the content, which is illegal in a CDATA section per section 2.7 of [XML 1.0]. The presence of this character
  sequence must generate a fatal error during serialization.

  Note: Because no markup is recognized within a xmlCDATASection, character numeric references cannot be used as an
  escape mechanism when serializing. Therefore, action needs to be taken when serializing a CDATASection with a
  character encoding where some of the contained characters cannot be represented. Failure to do so would not produce
  well-formed XML.
 
  One potential solution in the serialization process is to end the CDATA section before the character, output the
  character using a character reference or entity reference, and open a new CDATA section for any further characters in
  the text node. Note, however, that some code conversion libraries at the time of writing do not return an error or
  exception when a character is missing from the encoding, making the task of ensuring that data is not corrupted on
  serialization more difficult.
*/
 
::class xmlCDATASection public subclass xmlText
/**
  The CDATASection instance constructot
  @param content - The unparsed string representing the contents of the CDATASection.
*/

::method init
  use strict arg content
  self~init:super(content)
return
/* :end */

/* CLASS: xmlPI */
/**
  The ProcessingInstruction interface represents a "processing instruction", used in XML as a way to keep
  processor-specific information in the text of the document.

  No lexical check is done on the content of a processing instruction and it is therefore possible to have the character
  sequence "?>" in the content, which is illegal a processing instruction per section 2.6 of [XML 1.0]. The presence of
  this character sequence must generate a fatal error during serialization.
*/

::class xmlPI public subclass xmlNode
/**
  The content of this processing instruction. This is from the first non white space character after the target to the
  character immediately preceding the ?>.
*/

::method data
return self~nodeValue
/**
  The target of this processing instruction. XML defines this as being the first token following the markup that begins
  the processing instruction.
*/

::method target
return self~nodeName
/**
  The xmlProcessingInstruction instance constructor
  @param target - The target for the processing instruction
  @param data - The unparsed string representing the content of the processing instruction.
*/

::method init
  use strict arg target, data
  self~init:super
  self~nodeType = .xmlNode~processingInstructionNode
  self~nodeName = target
  self~nodeValue = data
return
/* :end */

/* CLASS: xmlDocumentType */
/**
  Information about the notations and entities declared by a document (including the external subset if the parser uses
  it and can provide the information) is available from a xmlDocumentType object. The xmlDocumentType for a document is
  available from the xmlDocument objectís doctype attribute; if there is no DOCTYPE declaration for the document, the
  documentís doctype attribute will be set to <b>.nil</b> instead of an instance of this interface.

  xmlDocumentType is a subclass (precisation) of xmlNode, and adds the following attributes:
  <ul>
  <li>entities - This is a xmlNamedNodeMap giving the definitions of external and internal entities. For entity names
  defined more than once, only the first definition is provided (others are ignored as required by the XML
  recommendation). This may be <b>.nil</b> if the information is not provided by the parser, or if no entities are
  defined.</li>
  <li>internalSubset - A string giving the complete internal subset from the document. This does not include the
  brackets which enclose the subset. If the document has no internal subset, this is <b>.nil</b>.</li>
  <li>name - The name of the root element as given in the DOCTYPE declaration, if present.</li>
  <li>notations - This is a xmlNamedNodeMap giving the definitions of notations. For notation names defined more than
  once, only the first definition is provided (others are ignored as required by the XML recommendation). This may be
  <b>.nil</b> if the information is not provided by the parser, or if no notations are defined.</li>
  <li>publicId - The public identifier for the external subset of the document type definition. This will be a string
  or <b>.nil</b>.</li>
  <li>systemId - The system identifier for the external subset of the document type definition. This will be a URI as a
  string, or <b>.nil</b>.</li>
  </ul>

  DOM Level 3 doesn't support editing DocumentType nodes. DocumentType nodes are read-only.
*/

::class xmlDocumentType public subclass xmlNode
/**
  Entities is a xmlNamedNodeMap containing the general entities, both external and internal, declared in the DTD.
  Parameter entities are not contained. Every node in this map also implements the Entity interface.
  The DOM Level 2 does not support editing entities, therefore entities cannot be altered in any way.
*/

::attribute entities get
::attribute entities set
/**
  The internal subset as a string, or <b>.nil</b> if there is none. This does not contain the delimiting square
  brackets.
  <b><u>NOTE</u>,</b> - The actual content returned depends on how much information is available to the implementation.
  This may vary depending on various parameters, including the XML processor used to build the document.
*/

::attribute internalSubset get
::attribute internalSubset set
/**
  The name of DTD; i.e., the name immediately following the DOCTYPE keyword.
*/

::attribute name get
::attribute name set
/**
  Notations is xmlNamedNodeMap containing the notations declared in the DTD. Duplicates are discarded. Every node in
  this map also implements the xmlNotation interface.
  The DOM Level 2 does not support editing notations, therefore notations cannot be altered in any way.
*/

::attribute notations get
::attribute notations set
/**
  The public identifier of the external subset.
*/

::attribute publicId get
::attribute publicId set
/**
  The system identifier of the external subset. This may be an absolute URI or not.
*/

::attribute systemId get
::attribute systemId set
/**
  xmlDocumentType instance constructor.
  @param name - The qualified name of the document (e.g. 'gpx'), is equal to the root tagName.
  @param publicId - The public identifier of this document type (e.g. '-//W3C//DTD XHTML 1.0 Strict//EN').
  @param systemId - The system identifier for the docType (e.g. 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd').
*/

::method init
  use strict arg name, publicId, systemId
  self~nodeType = 10
  self~nodeName = name
  self~publicId = publicId
  self~systemId = systemId
  self~entities = .NamedNodeMap~new
  self~notations = .NamedNodeMap~new
  self~internalSubset = .nil
return
/* :end */

/* CLASS: xmlElement */
/**
  The Element interface represents an element in an HTML or XML document. Elements may have attributes associated with
  them; since the Element interface inherits from Node, the generic Node interface attribute attributes may be used to
  retrieve the set of all attributes for an element. There are methods on the Element interface to retrieve either an
  Attr object by name or an attribute value by name. In XML, where an attribute value may contain entity references, an
  Attr object should be retrieved to examine the possibly fairly complex sub-tree representing the attribute value. On
  the other hand, in HTML, where all attributes have simple string values, methods to directly access an attribute value
  can safely be used as a convenience.

  Note: In DOM Level 2, the method normalize is inherited from the Node interface where it was moved.
*/

::class xmlElement public subclass xmlNode
/**
  Private method to walk the xmlNode tree and retrieve all elements with the specified tagName.
  @param nodes - the xmlNodes to start with, i.e. the childNodes from the receiver node.
  @param tag - The name odf the tag for the elements to be selected,'*' means all tags.
  @param nodeList - A xmlNodeList with all the selected elements in the subtree with specified tagName.
*/

::method selectElements private
  use strict arg nodes, tag, nodeList
  do n=0 to nodes~length-1
    node = nodes~item(n)
    if node~nodeType==.xmlNode~elementNode then do
      if tag<>'*' then do
        if node~tagName==tag then nodeList~append(node)
      end
      else do
        nodeList~append(node)
      end
      if node~childNodes~length>0 then do
        self~selectElements(node~childNodes, tag, nodeList)
      end
    end
  end
return
/**
  Private method to walk the xmlNode tree and retrieve all elements from a namespace with the specified local name.
  @param nodes - the xmlNodes to start with, i.e. the childNodes from the receiver node.
  @param namespaceURI - The namespace of the elements to be selected.
  @param localName - The local name for the element to be selected.
  @param nodeList - A xmlNodeList with all the elements in the subtree with specified tagName.
*/

::method selectElementsNS private
  use strict arg nodes, namespaceURI, localName, nodeList
  do n=0 to nodes~items-1
    node = nodes[n]
    if node~nodeType==.xmlNode~elementNode then do
      if localName<>'*' then do
        if node~localName==localName then do
          if namespaceURI<>'*' then do
            if node~namespaceURI==namespacURI then nodeList~append(node)
          end
          else do
            nodeList~append(node)
          end
        end
        else do
          nop
        end
      end
      else do
        if namespaceURI<>'*' then do
          if node~namespaceURI==namespacURI then nodeList~append(node)
        end
        else do
          nodeList~append(node)
        end
      end
    end
    if node~childNodes~items>0 then do
      self~getElementNodesByTagNS(node~childNodes, namespaceURI, localName, nodeList)
    end
  end
return
/**
  The type information associated with this element.
*/

::method schemaTypeInfo
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  The name of the element. If Node <i>localName</i> is different from null, this attribute is a qualified name.
*/

::method tagName
return self~nodeName
/**
  xmlElement instance constructor
  @param tagName - The name of the xmlElement to be created.
 
  nodeValue, localName, prefix, namespaceURI and childNodes are set by the superclass xmlNode
*/

::method init
  use strict arg tagName
  self~init:super
  self~nodeName = tagname
  self~nodeType = .xmlNode~elementNode
  self~attributes = .xmlNamedNodeMap~new
  --self~nodeValue = .nil
  --self~localName = .nil
  --self~prefix = .nil
  --self~namespaceURI = .nil
  --self~childNodes = xmlNodeList~new
return
/**
  Retrieves an attribute value by name.
  @param name - The name of the attribute to retrieve.
  @return string - The xmlAttr value or <b>.nil</b> if that attribute does not have a specified or default value.
*/

::method getAttribute
use strict arg name
nodeValue = .nil
node = self~attributes~getNamedItem(name)
if node<>.nil then nodeValue = node~nodeValue
return nodeValue
/**
  Retrieves an attribute value by local name and namespace URI.
  @param namespaceURI=.nil - The namespace URI of the attribute to retrieve.
  @param localName - The local name of the attribute to retrieve.
  @return string - The xmlAttr value, or the empty string if that attribute does not have a specified or default value.
 
  Per [XML Namespaces], applications must use the value null as the namespaceURI parameter for methods if they wish to
  have no namespace.
 
  Assumption: Attributes can not have a default namespace, thus need a prefix if they belong to a certain namespace.
  Assumption: A namespace can have multiple prefixes.
*/

::method getAttributeNS
  use strict arg namespaceURI=.nil, localName
  nodeValue = .nil
  if namespaceURI==.nil then do
    node = self~attributes~getNamedItem(localName)
    if node<>.nil then nodeValue = node~nodeValue
  end
  else do
    prefixes = self~ownerdocument~nameSpaces[namespaceURI]
    found = .false
    if prefixes<>.nil then do i=1 to prefix~words until found
      prefix = prefixes~word(i)
      node = self~attributes~getNamedItem(prefix":"localName)
        if node<>.nil then do
          nodeValue = node~nodeValue
          found = .true
      end
    end
  end
return nodeValue
/**
  Retrieves an attribute node by name.
  @param name - The nodeName of the attribute to retrieve
  @return node - The xmlAttr node with the specified nodeName or <b>.nil</b> if there is no such attribute.
 
  To retrieve an attribute node by qualified name and namespace URI, use the getAttributeNodeNS method.
*/

::method getAttributeNode
use strict arg name
attrNode = self~attributes~getNamedItem(name)
return attrNode
/**
  Retrieves an Attr node by local name and namespace URI.
  @param namespaceURI=.nil - The namespace URI of the attribute to retrieve.
  @param localName - The local name of the attribute to retrieve.
  @return node - The xmlAttr node with the specified attribute local name and namespace URI or null if there is no such
  attribute.

  Per [XML Namespaces], applications must use the value null as the namespaceURI parameter for methods if they wish to
  have no namespace.

  Assumption: Attributes can not have a default namespace, thus need a prefix if they belong to a certain namespace.
  Assumption: A namespace can have multiple prefixes.
*/

::method getAttributeNodeNS
  use strict arg namespaceURI=.nil, localName
  if namespaceURI==.nil then do
    attrNode = self~attributes~getNamedItem(localName)
  end
  else do
    prefixes = self~ownerdocument~nameSpaces[namespaceURI]
    found = .false
    if prefixes<>.nil then do i=1 to prefix~words until found
      prefix = prefixes~word(i)
      attrNode = self~attributes~getNamedItem(prefix":"localName)
      if attrNode<>.nil then found = .true
    end
  end
return attrNode
/**
  Returns a xmlNodeList of all descendant xmlElements with a given tag name, in document order.
  @param tag - The name of the tag to match on. The special value "*" matches all tags.
  @return nodeList - A list of matching xmlElement nodes.
*/

::method getElementsByTagName
  use strict arg tag
  nodeList = .xmlNodeList~new
  self~selectElements(self~childNodes, tag, nodeList)
return nodeList
/**
  Returns true when an attribute with a given name is specified on this element or has a default value, false otherwise.
  @param name - The name of the attribute to look for.
  @return boolean - <b>.true</b> if an attribute with the given name is specified on this element or has a default
  value, <b>.false</b> otherwise.
*/
 
::method hasAttribute
  use strict arg name
return self~attributes~getNamedItem(name)<>.nil
/**
  Returns true when an attribute with a given local name and namespace URI is specified on this element or has a default
  value, false otherwise.
  @param namespaceURI=.nil - The namespace URI of the attribute to look for.
  @param localName - The local name of the attribute to look for.
  @return boolean - <b>.true</b> if an attribute with the given local name and namespace URI is specified or has a
  default value on this element, <b>.false</b> otherwise.
   
  Per [XML Namespaces], applications must use the value <b>.nil</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.

  Assumption: Attributes can not have a default namespace, thus need a prefix if they belong to a certain namespace.
  Assumption: A namespace can have multiple prefixes.
  */

::method hasAttributeNS
  use strict arg namespaceURI=.nil, localName
  boolean = .false
  if namespaceURI==.nil then do
    boolean = self~attributes~getNamedItem(name)<>.nil
  end
  prefixes = self~ownerDocument~nameSpaces[namespaceURI]
  if prefixes<>.nil then do
    do p=1 to prefixes~words until boolean==.true
      prefix = prefixes~word(p)
      boolean = self~attributes~getNamedItem(prefix':'localName)<>.nil
    end
  end
return boolean
/**
  Removes an attribute by name.
  NOT supported here:
  (If a default value for the removed attribute is defined in the DTD, a new attribute immediately appears with the
  default value as well as the corresponding namespace URI, local name, and prefix when applicable. The implementation
  may handle default values from other schemas similarly but applications should use Document.normalizeDocument() to
  guarantee this information is up-to-date.)
  @param name - The name of the attribute to remove.

  If no attribute with this name is found, this method has no effect.
  To remove an attribute by local name and namespace URI, use the removeAttributeNS method.
*/

::method removeAttribute
use strict arg name
removed = self~attributes~removeNamedItem(name)
return
/**
  Removes an attribute by local name and namespace URI.
  NOT supported here:
  (If a default value for the removed attribute is defined in the DTD, a new attribute immediately appears with the
  default value as well as the corresponding namespace URI, local name, and prefix when applicable. The implementation
  may handle default values from other schemas similarly but applications should use Document.normalizeDocument() to
  guarantee this information is up-to-date.)
  @param nameSpaceURI=.nil - The namespace URI of the attribute to remove.
  @param localName - The local name of the attribute to remove.

  If no attribute with this local name and namespace URI is found, this method has no effect.
 
  Per [XML Namespaces], applications must use the value <b>.nil</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.
*/

::method removeAttributeNS
  use strict arg nameSpaceURI=.nil, localName
  removed = self~attributes~removeNamedItemNS(namespaceURI,localName)
return
/**
  Removes the specified attribute node.
  NOT supported here:
  (If a default value for the removed Attr node is defined in the DTD, a new node immediately appears with the default
  value as well as the corresponding namespace URI, local name, and prefix when applicable. The implementation may
  handle default values from other schemas similarly but applications should use Document.normalizeDocument() to
  guarantee this information is up-to-date.)
  @param oldAttr - The reference to the xmlAttr to be removed.
  @return attr - The removed attribute.
  @raises user domException - NOT_FOUND_ERR perhaps
*/

::method removeAttributeNode
  use strict arg oldAttr
  removed = self~attributes~removeNamedItem(oldAttr~nodeName)
return removed
/**
  Adds a new attribute. If an attribute with that name is already present in the element, its value is changed to be
  that of the value parameter. This value is a simple string; it is not parsed as it is being set. So any markup (such
  as syntax to be recognized as an entity reference) is treated as literal text, and needs to be appropriately escaped
  by the implementation when it is written out. In order to assign an attribute value that contains entity references,
  the user must create an xmlAttr node plus any xmlText and xmlEntityReference nodes, build the appropriate subtree, and
  use <i>setAttributeNode</i> to assign it as the value of an attribute.
  @param name - The name of the attribute to create or alter.
  @param value - Value to set in string form.
  @raises user domException - INVALID_CHARACTER_ERR
 
  To set an attribute with a qualified name and namespace URI, use the setAttributeNS method.
*/

::method setAttribute
  use strict arg name, value
  if name~verify(xmlValidName())>0 then do
    raise user domException description (.domException~invalid_character_err)
  end
  node = self~attributes~getNamedItem(name)
  if node<>.nil then do
    node~nodeValue = value
  end
  else do
    node = xmlAttr~new
    node~nodeName = name
    node~nodeValue = value
    self~attributes~setNamedItem(node)
  end
return
/**
  Adds a new attribute. If an attribute with the same local name and namespace URI is already present on the element,
  its prefix is changed to be the prefix part of the qualifiedName, and its value is changed to be the value parameter.
  This value is a simple string; it is not parsed as it is being set. So any markup (such as syntax to be recognized as
  an entity reference) is treated as literal text, and needs to be appropriately escaped by the implementation when it
  is written out. In order to assign an attribute value that contains entity references, the user must create an Attr
  node plus any Text and EntityReference nodes, build the appropriate subtree, and use <i>setAttributeNodeNS</i> or
  <i>setAttributeNode</i> to assign it as the value of an attribute.
  @param namespaceURI=.nil - The namespace URI of the attribute to create or alter.
  @param qualifiedName - The qualified name of the attribute to create or alter.
  @param value - The value to set in string form.
  @raises user domException - INVALID_CHARACTER_ERR
  @raises user domException - NAMESPACE_ERR
 
  Per [XML Namespaces], applications must use the value <b>.nil</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.
*/

::method setAttributeNS
  use strict arg namespaceURI=.nil, qualifiedName, value
  if qualifiedName~verify(xmlValidName())>0 then do
    raise user domException description (.domException~invalid_character_err)
  end
  if qualifiedName~pos(':')>0 & namespaceURI==.nil then do
    raise user domException description (.domException~namespace_err)
  end
  if qualifiedName~substr(1,3)~lower=="xml" & namespaceURI<>"http://www.w3.org/XML/1998/namespace" then do
    raise user domException description (.domException~namespace_err)
  end
  if qualifiedName~substr(1,5)~lower=="xmlns" & namespaceURI<>"http://www.w3.org/2000/xmlns/" then do
    raise user domException description (.domException~namespace_err)
  end
  if qualifiedName~substr(1,5)~lower<>"xmlns" & namespaceURI=="http://www.w3.org/2000/xmlns/" then do
    raise user domException description (.domException~namespace_err)
  end
  localName = qualifiedName
  if quailifedName~pos(':')>0 then parse var qualifiedName prefix ':'localName
  node = self~attributes~getNamedItemNS(namespaceURI,localName)
  if node<>.nil then do
    node~nodeValue = value
  end
  else do
    node = xmlAttr~new
    node~nodeName = name
    node~nodeValue = value
    node~namespaceURI = namespaceURI
    node~localName = localName
    node~prefix = prefix
    self~attributes~setNamedItemNS(node)
  end
return
/**
  Adds a new attribute node. If an attribute with that name (nodeName) is already present in the element, it is replaced
  by the new one. Replacing an attribute node by itself has no effect.
  @param newAttr - The xmlAttr node to add to the attribute list.
  @return attr - If the newAttr attribute replaces an existing attribute, the replaced Attr node is returned, otherwise
  <b>.nil</b> is returned.
  @raises user domExcep[tion - WRONG_DOCUMENT_ERR
  @raises user domException - INUSE_ATTRIBUTE_ERR
 
  To add a new attribute node with a qualified name and namespace URI, use the setAttributeNodeNS method.
*/

::method setAttributeNode
  use strict arg newAttr
  if newAttr~ownerDocument<>self~ownerDocument then do
    raise user domException additional (.domException~wrong_document_err) description ("Attribute" newAttr~name "is owned by another document")
  end
  if newAttr~ownerElement<>self then do
    raise user domException additional (.domException~inuse_attribute_err) description (newAttr~ownerElement)
  end
  node = self~attributes~setNamedItem(newAttr)
return node
/**
  Adds a new attribute. If an attribute with that local name and that namespace URI is already present in the element,
  it is replaced by the new one. Replacing an attribute node by itself has no effect.
  @param newAttr - The xmlAttr node to add to the attribute list.
  @return attr - If the newAttr attribute replaces an existing attribute, the replaced Attr node is returned, otherwise
  <b>.nil</b> is returned.
  @raises user domExcep[tion - WRONG_DOCUMENT_ERR
  @raises user domException - INUSE_ATTRIBUTE_ERR
 
  Per [XML Namespaces], applications must use the value <b>.nil</b> as the namespaceURI parameter for methods if they
  wish to have no namespace.
*/

::method setAttributeNodeNS
  use strict arg newAttr
  node = self~attributes~setNamedItemNS(newAttr)
return node
/**
  If the parameter isId is true, this method declares the specified attribute to be a user-determined ID attribute. This
  affects the value of the xmlAttr <i>isId</i> meyhod and the behavior of the xmlDocument <i>getElementById</i> method,
  but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
  <i>schemaTypeInfo</i> property of the specified xmlAttr node.
  Use the value <b>.false</b> for the parameter isId to undeclare an attribute for being a user-determined ID attribute.
  @param name - The name of the attribute.
  @param isId - Whether the attribute is a of type ID.
  @raises user domException - NOT_FOUND_ERR
 
  To specify an attribute by local name and namespace URI, use the setIdAttributeNS method.
*/

::method setIdAttribute
  use strict arg name, isId
  node = self~attributes~getNamedItem(name)
  if node==.nil then do
    raise user domException description (.domException~not_found_err)
  end
  node~isId = isId
return
/**
  If the parameter isId is true, this method declares the specified attribute to be a user-determined ID attribute. This
  affects the value of the xmlAttr <i>isId</i> meyhod and the behavior of the xmlDocument <i>getElementById</i> method,
  but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
  <i>schemaTypeInfo</i> property of the specified xmlAttr node.
  Use the value <b>.false</b> for the parameter isId to undeclare an attribute for being a user-determined ID attribute.
  @param namespaceURI - The namespace URI of the attribute.
  @param localName - The local name of the attribute.
  @param isId - Whether the attribute is a of type ID.
  @raises user domException - NOT_FOUND_ERR
*/

::method setIdAttributeNS
  use strict arg namespaceURI, localName, isId
  node = self~attributes~getNamedItemNS(namespaceURI, localName)
  if node==.nil then do
    raise user domException description (.domException~not_found_err)
  end
  node~isId = isId
return
/**
  If the parameter isId is true, this method declares the specified attribute to be a user-determined ID attribute. This
  affects the value of the xmlAttr <i>isId</i> meyhod and the behavior of the xmlDocument <i>getElementById</i> method,
  but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
  <i>schemaTypeInfo</i> property of the specified xmlAttr node.
  Use the value <b>.false</b> for the parameter isId to undeclare an attribute for being a user-determined ID attribute.
  @param idAttr -
  @param isId -
  @raises user domException - NOT_FOUND_ERR
*/

::method setIdAttributeNode
  use strict arg idAttr, isId
  if idAttr~ownerElement<>self then do
    raise user domException description(.domException~not_found_err)
  end
  idAttr~isId = isId
return
/* :end */

/* CLASS: xmlDocument */
/**
  The xmlDocument interface represents the entire HTML or XML document. Conceptually, it is the root of the document
  tree, and provides the primary access to the document's data.

  Since elements, text nodes, comments, processing instructions, etc. cannot exist outside the context of a Document,
  the xmlDocument interface also contains the factory methods needed to create these objects. The xmlNode objects
  created have a ownerDocument attribute which associates them with the Document within whose context they were created.
*/

::class xmlDocument public subclass xmlNode
/**
  A directory with elementIds as index and the associated xmlElement as item.
*/

::attribute elementIds private
/**
  A directory with tagNames as index and an array of xmlElement objects with the tagName as objects
*/

::attribute elementTags private
/**
  A directory with namespaceURIs as index and their prefix as item
*/

::attribute nameSpaces private
/**
  The Document Type Declaration (see DocumentType) associated with this document. For XML documents without a document
  type declaration this returns <b>.nil</b>. For HTML documents, a DocumentType object may be returned, independently of
  the presence or absence of document type declaration in the HTML document.
 
  This provides direct access to the xmlDocumentType node, child node of this xmlDocument. This node can be set a
  t document creation time and later changed through the use of child nodes manipulation methods, such as xmlNode's
  <i>insertBefore</i>, or xmlNode's <i>replaceChild</i>.
 
  Note, however, that while some implementations may instantiate different types of Document objects supporting
  additional features than the "Core", such as "HTML" [DOM Level 2 HTML], based on the xmlDocumentType specified at
  creation time, changing it afterwards is very unlikely to result in a change of the features supported.
*/

::attribute docType get
/**
*/

::attribute docType set
/**
  This is a convenience attribute that allows direct access to the child node that is the document element of
  the document.
*/

::attribute documentElement get
/**
*/

::attribute documentElement set
/**
  The location of the document or <b>.nil</b> if undefined or if the Document was created using xmlDOMImplementation
  <i>createDocument</i>. No lexical checking is performed when setting this attribute; this could result in a
  <b>.nil</b> value returned when using xmlNode <i>baseURI</i>.
 
  Beware that when the Document supports the feature "HTML" [DOM Level 2 HTML], the href attribute of the HTML BASE
  element takes precedence over this attribute when computing xmlNode <i>baseURI</i>.
*/

::attribute documentURI
/**
  The configuration used when xmlDocument <i>normalizeDocument</i> is invoked.
*/

::attribute domConfig get
/**
*/

::attribute domConfig set
/**
  The xmlDOMImplementation object that handles this document. A DOM application may use objects from multiple
  implementations.
  The attributes setter method ("IMPLEMENTATION=") starts out as <i>public</i> and is set to <i>private</i> once set
  turning the attribute into a <i>read-only</i>.
*/

::attribute implementation get
::attribute implementation set
/**
  An attribute specifying the encoding used for this document at the time of the parsing. This is null when it is not
  known, such as when the xmlDocument was created in memory.
*/

::attribute inputEncoding get
::attribute inputEncoding set
/**
  An attribute specifying whether error checking is enforced or not. When set to <b>.false</b>, the implementation is
  free to not test every possible error case normally defined on DOM operations, and not raise any xmlDOMException on
  DOM operations or report errors while using xmlDocument <i>normalizeDocument</i>. In case of error, the behavior is
  undefined. This attribute is true by default.
*/

::attribute strictErrorChecking
/**
  An attribute specifying, as part of the XML declaration, the encoding of this document. This is <b>.nil</b> when
  unspecified or when it is not known, such as when the Document was created in memory.
*/

::attribute xmlEncoding
/**
  An attribute specifying, as part of the XML declaration, whether this document is standalone. This is <b>.false</b>
  when unspecified.
 
  Note: No verification is done on the value when setting this attribute. Applications should use xmlDocument
  <i>normalizeDocument</i> with the "validate" parameter to verify if the value matches the validity constraint for
  standalone document declaration as defined in [XML 1.0].
*/

::attribute xmlStandalone
/**
  An attribute specifying, as part of the XML declaration, the version number of this document. If there is no
  declaration and if this document supports the "XML" feature, the value is "1.0". If this document does not support the
  "XML" feature, the value is always null. Changing this attribute will affect methods that check for invalid characters
  in XML names. Application should invoke xmlDocument <i>normalizeDocument</i> in order to check for invalid characters
  in the Nodes that are already part of this Document.
 
  DOM applications may use the xmlDOMImplementation <i>hasFeature(feature, version)</i> method with parameter values
  "XMLVersion" and "1.0" (respectively) to determine if an implementation supports [XML 1.0]. DOM applications may use
  the same method with parameter values "XMLVersion" and "1.1" (respectively) to determine if an implementation supports
  [XML 1.1]. In both cases, in order to support XML, an implementation must also support the "XML" feature defined in
  this specification. Document objects supporting a version of the "XMLVersion" feature must not raise a
  NOT_SUPPORTED_ERR exception for the same version number when using xmlDocument <i>xmlVersion</i>.
 
  @raises user domException - NOT_SUPPORTED_ERR

  Raised if the version is set to a value that is not supported by this Document or if this document does not support
  the "XML" feature.  
*/

::attribute xmlVersion
/**
  xmlDocument instance constructor
*/

::method init
  -- public attributes
  self~init:super
  self~nodeType = .xmlNode~documentNode
  self~nodeName = "#document"
  self~nodeValue = .nil
  self~attributes = .nil
  self~parentNode = .nil
  self~previousSibling = .nil
  self~nextSibling = .nil
  self~ownerDocument = .nil
  --self~docType = .nil same as implementation
  --self~implementation = .xmlDomImplementation~new -- This one is set by .xmlDomImplementation <i>createDocument</i> method
  -- private attributes
  self~elementIds = .directory~new
  self~elementTags = .directory~new
  self~nameSpaces = .directory~new
return
/**
  Attempts to adopt a node from another document to this document. If supported, it changes the ownerDocument of the
  source node, its children, as well as the attached attribute nodes if there are any. If the source node has a parent
  it is first removed from the child list of its parent. This effectively allows moving a subtree from one document to
  another (unlike <i>importNode</i> which create a copy of the source node instead of moving it). When it fails,
  applications should use xmlDocument <i>importNode</i> instead.
 
  Note that if the adopted node is already part of this document (i.e. the source and target document are the same),
  this method still has the effect of removing the source node from the child list of its parent, if any. The following
  list describes the specifics for each type of node.
  <ul>
  <li>ATTRIBUTE_NODE - The ownerElement attribute is set to null and the specified flag is set to true on the adopted
  xmlAttr. The descendants of the source xmlAttr are recursively adopted.</li>
  <li>DOCUMENT_FRAGMENT_NODE - The descendants of the source node are recursively adopted.</li>
  <li>DOCUMENT_NODE - Document nodes cannot be adopted.</li>
  <li>DOCUMENT_TYPE_NODE - DocumentType nodes cannot be adopted.</li>
  <li>ELEMENT_NODE - Specified attribute nodes of the source element are adopted. Default attributes are discarded,
  though if the document being adopted into defines default attributes for this element name, those are assigned. The
  descendants of the source element are recursively adopted.</li>
  <li>ENTITY_NODE - Entity nodes cannot be adopted.</li>
  <li>ENTITY_REFERENCE_NODE - Only the EntityReference node itself is adopted, the descendants are discarded, since the
  source and destination documents might have defined the entity differently. If the document being imported into
  provides a definition for this entity name, its value is assigned.</li>
  <li>NOTATION_NODE - Notation nodes cannot be adopted.</li>
  <li>PROCESSING_INSTRUCTION_NODE, TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE - These nodes can all be adopted. No
  specifics.</li>
  </ul>
 
  Note: Since it does not create new nodes unlike the xmlDocument <i>importNode</i> method, this method does not raise
  an INVALID_CHARACTER_ERR exception, and applications should use the xmlDocument <i>normalizeDocument</i> method to
  check if an imported name is not an XML name according to the XML version in use.
  @param source - The xmlNode to move into this document.
  @return node - The adopted node, or <b>.nil</b> if this operation fails, such as when the source node comes from a
  different implementation.
*/

::method adoptNode
  use strict arg source
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  Creates an xmlAttr of the given name. Note that the xmlAttr instance can then be set on an xmlElement using the
  <i>setAttributeNode</i> method.
  The new xmlAttr has the <i>nodeName</i> attribute set to <i>name</i>, and <i>localName</i>, <i>prefix</i>, and
  <i>namespaceURI</i> set to null. The value of the attribute is the empty string.
  @param name - The name of the attribute.
  @param value=.nil - The (optional) value of the attribute
  @return attr - A new Attr object
  @raises user domException - INVALID_CHARACTER_ERR
 
  Raised if the specified name is not an XML name according to the XML version in use specified in the xmlDocument
  <i>xmlVersion</i> attribute.
  To create an attribute with a qualified name and namespace URI, use the <i>createAttributeNS</i> method.
*/

::method createAttribute
  use strict arg name, value=.nil
  if name~verify(xmlValidName())>0
    then raise user domException description (.domException~invalid_character_err)
  attr = .xmlAttr~new
  --attr~name = name
  attr~nodeName = name
  attr~localName = .nil
  attr~prefix = .nil
  attr~namespaceURI = .nil
  attr~nodeValue = value
  --attr~value = .nil
  attr~ownerDocument = self
return attr
/**
  Creates an attribute of the given qualified name and namespace URI.
  @param namespaceURI=.nil - The optional namespace URI of the attribute to create.
  @param qualifiedName - The qualified name of the attribute to instantiate.
  @return attr - A new xmlAttr object with the following attributes:
 
  <ul>
  <li>nodeName - The specified qualifiedName.</li>
  <li>namespaceURI - The specified namespaceURI.</li>
  <li>prefix - prefix, extracted from qualifiedName, or null if there is no prefix.</li>
  <li>localName - name, extracted from the specified qualifiedName.</li>
  <li>name - The specified qualifiedName.</li>
  <li>nodeValue - An empty string (i.e. "").</li>
  </ul>
  @raises user domException - INVALID_CHARACTER_ERR
  @raises user domException - NAMESPACE_ERR
 
  Per [XML Namespaces], applications must use the value null as the namespaceURI parameter for methods if they wish to
  have no namespace.
*/

::method createAttributeNS
  use strict arg namespaceURI=.nil, qualifiedName
  if qualifiedName~verify(xmlValidName())>0
    then raise user domException description (.domException~invalid_character_err)
  exception = .false
  if qualifiedName~countStr(':')>1 then exception = .true
  prefix = ''
  localName = qualifiedName
  if qualifiedName~countStr(':')==1 then do
    parse var qualifiedName prefix ':' localName
    if prefix=='XML' & namesapceURI<>"http://www.w3.org/XML/1998/namespace" then exception = .true
    -- 2 more conditions to check, will be todo
  end
  if exception==.true then raise user domException description (.domException~namespace_err)
  attr = .xmlAttr~new
  attr~ nodeName = qualifiedName
  attr~namespaceURI = namespaceURI
  attr~prefix = prefix
  attr~localName = localName
  attr~name = qualifiedName
  attr~nodeValue = ''
return attr
/**
  Creates a xmlCDATASection node whose value is the specified string.
  @param data - The data for the node.
  @return cdata - The new xmlCDATASection object.
*/

::method createCDATASection
  use strict arg data
  cdata = .xmlCDATASection~new(data)
return cdata
/**
  Creates a xmlComment node given the specified string.
  @param data - The data for the node
  @return comment - The new xmlComment object
*/

::method createComment
  use strict arg data
  comment = .xmlComment~new(data)
return comment
/**
  Creates an empty DocumentFragment object.
  @return docFragment - The newly created empty xmlDocumentFragment
*/

::method createDocumentFragment
  use strict arg
  docFragment = .xmlDocumentFragment~new
return docFragment
/**
  Creates an element of the type specified. Note that the instance returned implements the Element interface, so
  attributes can be specified directly on the returned object. The new xmlElement object will have the nodeName
  attribute set to <i>tagName</i>, and localName, prefix, and namespaceURI set to null.
  @param tagName - The name(case-sensitive for XML) of the element type to instantiate.
 
  NOT YET!!!!!
  In addition, if there are known attributes with default values, xmlAttr nodes representing them are automatically
  created and attached to the element.
  @return element - A new xmlElement as described above.
  @raises user domExceptuion - INVALID_CHARACTER_ERR
 
  To create an element with a qualified name and namespace URI, use the createElementNS method.
*/

::method createElement
  use strict arg tagName
  if tagName~verify(xmlValidName())>0
    then raise user domException description (.domException~invalid_character_err)
  element = .xmlElement~new(tagName)
  element~ownerDocument = self
  --if self~elementTags[tagName]==.nil then self~elementTags[tagName] = .xmlNodelist~new
  --self~elementTags[tagName]~append(element)
return element
/**
  Creates an element of the given qualified name and namespace URI.
  @param namespaceURI=.nil - The optional namespace URI of the element to create.
  @param qualifiedName - The qualified name of the element type to instantiate.
  @return attr - A new xmlAttr object with the following attributes:
 
  <ul>
  <li>nodeName - The specified qualifiedName.</li>
  <li>namespaceURI - The specified namespaceURI.</li>
  <li>prefix - prefix, extracted from qualifiedName, or null if there is no prefix.</li>
  <li>localName - name, extracted from the specified qualifiedName.</li>
  <li>tagName - The specified qualifiedName.</li>
  </ul>
  @raises user domException - INVALID_CHARACTER_ERR
  @raises user domException - NAMESPACE_ERR
 
 
  Per [XML Namespaces], applications must use the value null as the namespaceURI parameter for methods if they wish to
  have no namespace.
*/

::method createElementNS
  use strict arg namespaceURI=.nil, qualifiedName
  if qualifiedName~verify(xmlValidName())>0
    then raise user domException description (.domException~invalid_character_err)
  exception = .false
  if qualifiedName~countStr(':')>1 then exception = .true
  prefix = ''
  localName = qualifiedName
  if qualifiedName~countStr(':')==1 then do
    parse var qualifiedName prefix ':' localName
    if prefix=='XML' & namesapceURI<>"http://www.w3.org/XML/1998/namespace" then exception = .true
    -- more conditions to check, will be todo
  end
  if exception==.true then raise user domException description (.domException~namespace_err)
  element = .xmlElement~new
  element~nodeName = qualifiedName
  element~namespaceURI = namespaceURI
  element~prefix = prefix
  element~localName = localName
  element~tagName = qualifiedName
return element
/**
  Creates a xmlEntityReference object. In addition, if the referenced entity is known, the child list of the
  xmlEntityReference node is made the same as that of the corresponding xmlEntity node.
  @param name - The name of the reference
 
  Note: If any descendant of the Entity node has an unbound namespace prefix, the corresponding descendant of the
  created EntityReference node is also unbound; (its namespaceURI is null). The DOM Level 2 and 3 do not support any
  mechanism to resolve namespace prefixes in this case.
*/

::method createEntityReference
  use strict arg name
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  Creates a xmlProcessingInstruction node given the specified target and data strings.
  @param target - The target part of the processing instruction
  @param data - The data for the node
  @return pi - The new xmlProcessingInstruction object
  @raises user domException - INVALID_CHARACTER_ERR
*/

::method createProcessingInstruction
  use strict arg target, data
  if target~verify(xmlValidName())>0
    then raise user domException description (.domException~invalid_character_err)
  pi = .xmlPI~new(target, data)
return pi
/**
  Creates a xmlText node given the specified string.
  @param data - The data for the node
  @return text - The new xmlText object
*/

::method createTextNode
  use arg data
  text = .xmlText~new(data)
return text
/**
  Returns the xmlElement that has an ID attribute with the given value. If no such element exists, this returns null. If
  more than one element has an ID attribute with that value, what is returned is undefined.

  <b.<u>NOTE</u></b> - Attributes with the name "ID" or "id" are not of type ID unless so defined.

  @param elementId - The unique id value for an element.
  @return element - The matching element or <b>.nil</b> if there is none.

  The DOM implementation is expected to use the attribute Attr~isId to determine if an attribute is of type ID.
*/

::method getElementById
  use strict arg elementId
  element = self~elementIds[elementId]
return element
/**
  Returns a xmlNodeList of all the xmlElements in document order with a given tag name and are within in the document.
  @param tagName - The name of the tag to match on. The special value "*" matches all tags.
  @return nodeList - A new xmlNodeList object containing all the matched xmlElements.

  For XML, the tagname parameter is case-sensitive, otherwise it depends on the case-sensitivity of the markup language
  in use.
*/

::method getElementsByTagName
  use strict arg tagName
  elements = .xmlNodeList~new
  self~selectElements(self~childNodes, tagName, elements)
return elements
/**
   Private method to walk the nodes tree and retrieve nodes by tagName
   @param nodes - The sibling nodes to walk thru.
   @param tag - The name of the tag to select on.
   @param nodelist - The xmlNodeList to append selected nodes to.
*/

::method selectElements private
  use strict arg nodes, tag, nodelist
  do n=0 to nodes~length-1
    node = nodes~item(n)
    if (node~nodeType==.xmlNode~elementNode) then do
      if tag<>'*' then do
        if (node~tagName==tag) then nodelist~append(node)
      end
      else do
        nodelist~append(node)
      end
      if (node~childNodes~length>0) then do
        self~selectElements(node~childNodes, tag, nodelist)
      end
    end
  end
return
/**
  Returns a xmlNodeList of all the xmlElements with a given local name and namespace URI in document order.
  @param namespaceURI=.nil - The namespace URI of the elements to match on. The special value "*" matches all namespaces.
  @param localName - The local name of the elements to match on. The special value "*" matches all local names.
  @return nodeList - A new xmlNodeList containing all the matched xmlElements.
*/

::method getElementsByTagNameNS
  use strict arg namespaceURI=.nil, localName
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  Imports a node from another document to this document, without altering or removing the source node from the original
  document; this method creates a new copy of the source node. The returned node has no parent; (parentNode is null).

  For all nodes, importing a node creates a node object owned by the importing document, with attribute values identical
  to the source node's nodeName and nodeType, plus the attributes related to namespaces (prefix, localName, and
  namespaceURI). As in the cloneNode operation, the source node is not altered. User data associated to the imported
  node is not carried over. However, if any UserDataHandlers has been specified along with the associated data these
  handlers will be called with the appropriate parameters before this method returns.
 
  Additional information is copied as appropriate to the nodeType, attempting to mirror the behavior expected if a
  fragment of XML or HTML source was copied from one document to another, recognizing that the two documents may have
  different DTDs in the XML case.
 
  The following list describes the specifics for each type of node.
  <ul>
  <li>ATTRIBUTE_NODE - The ownerElement attribute is set to null and the specified flag is set to true on the generated
  xmlAttr. The descendants of the source xmlAttr are recursively imported and the resulting nodes reassembled to form
  the corresponding subtree.
  <b><u>NOTE</u></b> - The deep parameter has no effect on xmlAttr nodes; they always carry their children with them
  when imported.</li>
  <li>DOCUMENT_FRAGMENT_NODE - If the deep option was set to true, the descendants of the source DocumentFragment are
  recursively imported and the resulting nodes reassembled under the imported DocumentFragment to form the corresponding
  subtree. Otherwise, this simply generates an empty DocumentFragment.</li>
  <li>DOCUMENT_NODE - Document nodes cannot be imported.</li>
  <li>DOCUMENT_TYPE_NODE - DocumentType nodes cannot be imported.</li>
  <li>ELEMENT_NODE - Specified attribute nodes of the source element are imported, and the generated xmlAttr nodes are
  attached to the generated xmlElement. Default attributes are not copied, though if the document being imported into
  defines default attributes for this element name, those are assigned. If the importNode deep parameter was set to
  true, the descendants of the source element are recursively imported and the resulting nodes reassembled to form the
  corresponding subtree.</li>
  <li>ENTITY_NODE - Entity nodes can be imported, however in the current release of the DOM the xmlDocumentType is
  readonly. Ability to add these imported nodes to a xmlDocumentType will be considered for addition to a future release
  of the DOM.</li>
  <li>ENTITY_REFERENCE_NODE - Only the EntityReference itself is copied, even if a deep import is requested, since the
  source and destination documents might have defined the entity differently. If the document being imported into
  provides a definition for this entity name, its value is assigned.</li>
  <li>NOTATION_NODE - Notation nodes can be imported, however they are read-only</li>
  <li>PROCESSING_INSTRUCTION_NODE - The imported node copies its target and data from those of the source node.</li>
  <li>TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE - These three types of nodes inheriting from xmlCharacterData copy
  their data and length attributes from those of the source node.</li>
  </ul>
  @param importNode - The node to import
  @param deep=.false - optional boolean to indicate a deep copy
  @return node - the imported node that belongs to this xmlDocument now.
  @raises user domException - INVALID_CHARACTER_ERR
*/

::method importNode
  use strict arg importNode, deep=.false
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  This method acts as if the document was going through a save and load cycle, putting the document in a "normal" form.
  As a consequence, this method updates the replacement tree of EntityReference nodes and normalizes Text nodes, as
  defined in the method xmlNode <i>normalize</i>.
 
  Otherwise, the actual result depends on the features being set on the Document.domConfig object and governing what
  operations actually take place. Noticeably this method could also make the document namespace well-formed according to
  the algorithm described in Namespace Normalization, check the character normalization, remove the CDATASection nodes,
  etc. See DOMConfiguration for details.

  Mutation events, when supported, are generated to reflect the changes occurring on the document.
 
  If errors occur during the invocation of this method, such as an attempt to update a read-only node or a Node.nodeName
  contains an invalid character according to the XML version in use, errors or warnings (DOMError.SEVERITY_ERROR or
  DOMError.SEVERITY_WARNING) will be reported using the DOMErrorHandler object associated with the "error-handler"
  parameter. Note this method might also report fatal errors (DOMError.SEVERITY_FATAL_ERROR) if an implementation cannot
  recover from an error.
*/

::method normalizeDocument
  use strict arg
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  Rename an existing node of type ELEMENT_NODE or ATTRIBUTE_NODE.
 
  When possible this simply changes the name of the given node, otherwise this creates a new node with the specified
  name and replaces the existing node with the new node as described below.
 
  If simply changing the name of the given node is not possible, the following operations are performed: a new node is
  created, any registered event listener is registered on the new node, any user data attached to the old node is
  removed from that node, the old node is removed from its parent if it has one, the children are moved to the new node,
  if the renamed node is an Element its attributes are moved to the new node, the new node is inserted at the position
  the old node used to have in its parent's child nodes list if it has one, the user data that was attached to the old
  node is attached to the new node.
 
  When the node being renamed is an Element only the specified attributes are moved, default attributes originated from
  the DTD are updated according to the new element name. In addition, the implementation may update default attributes
  from other schemas. Applications should use Document.normalizeDocument() to guarantee these attributes are up-to-date.
 
  When the node being renamed is an Attr that is attached to an Element, the node is first removed from the Element
  attributes map. Then, once renamed, either by modifying the existing node or creating a new one as described above, it
  is put back.
  In addition:
  <ul>
  <li>a user data event NODE_RENAMED is fired</li>
  <li>when the implementation supports the feature "MutationNameEvents", each mutation operation involved in this method
  fires the appropriate event, and in the end the event {http://www.w3.org/2001/xml-events, DOMElementNameChanged} or
  {http://www.w3.org/2001/xml-events, DOMAttributeNameChanged} is fired.</li>
  <\ul>
  @param node - The node to rename
  @param namespaceURI=.nil - The new namespace URI.
  @param qualifiedName - The new qualified name.
  @return node - The renamed node.
   
  The renamed node is either the specified node or the new node that was created to replace the specified node.
 
  @raises user domException - NOT_SUPPORTED_ERR
  @raises user domException - INVALID_CHARACTER_ERR
  @raises user domException - WRONG_DOCUMENT_ERR
  @raises user domException - NAMESPACE_ERR
*/

::method renameNode
  use strict arg node, namespaceURI=.nil, qualifiedName
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/* :end */

/* CLASS: xmlDomImplementation */
/**
  The xmlDomImplementation interface provides a number of methods for performing operations that are independent of any
  particular instance of the document object model.
*/

::class xmlDomImplementation public
::attribute features private
::method init
  use strict arg
  self~init:super
  self~features = .directory~new
  self~features["core"] = .directory~new~~putall(.array~of("",1.0,2.0))
  self~features["xml"] = .directory~new~~putall(.array~of("",1.0,2.0))
  self~features["ls-load"] = .directory~new~~putall(.array~of("",3.0))
return
/**
  Creates a DOM Document object of the specified type with its document element.
 
  Note that based on the DocumentType given to create the document, the implementation may instantiate specialized
  Document objects that support additional features than the "Core", such as "HTML" [DOM Level 2 HTML]. On the other
  hand, setting the DocumentType after the document was created makes this very unlikely to happen. Alternatively,
  specialized Document creation methods, such as createHTMLDocument [DOM Level 2 HTML], can be used to obtain specific
  types of Document objects.
  @param namespaceURI='' - The (optional) namespace URI of the document element to create or <b>.nil</b>.
  @param qualifiedName='' - The (optional) qualified name of the document element to be created or null.
  @param docType=.nil - The (optional) type of document to be created or <b..nil</b>.
  @return document - A new xmlDocument object with its document element.
  @raises user - domException.INVALID_CHARACTER_ERR
  @raises user - domException.NAMESPACE_ERR

  If the namespaceURI, qualifiedName, and docType are <b>.nil</b>, the returned xmlDocument is empty with no document
  element.
  When <b>docType</b> is not <b>.nil</b>, its <i>ownerDocument</i> attribute is set to the document being created.
*/

::method createDocument
  use strict arg namespaceURI='', qualifiedName='', docType=.nil
  if qualifiedName~verify(xmlValidName())>0
    then raise user domException description (.domException~INVALID_CHARACTER_ERR)
  prefix = ''
  localName = qualifiedName
  if qualifiedName~countStr(':')>1
    then raise user domException additional (.domException~namespace_err) description ("Qualified name can contain only one ':' character.")
  if qualifiedName~countStr(':')==1 then parse var qualifiedName prefix':'localName
  if prefix<>'' & namespaceURI==''
    then raise user domException additional (.domException~NAMESPACE_ERR) description ("Cannot have a prefix without <i>namespaceURI</i>.")
  if namespaceURI<>'' & qualifiedName==''
    then raise user domException additional (.domException~NAMESPACE_ERR) description ("Cannot have namespaceURI without prefix.")
  if prefix~upper=='XML' & namespaceURI~lower<>'http://www.w3.org/XML/1998/namespace'
    then raise user domException additional (.domException~NAMESPACE_ERR) description ("Invalid namespaceURI for prefix" prefix".")    
  if docType<>.nil then do
    if docType~isA(.xmlDocType) then do
      if docType~ownerDocument<>.nil then raise user domException additional (.domException~WRONG_DOCUMENT_ERR) description ("DOCTYTPE belongs to different document.")
    end
    else raise user domException additional (.domException~SYNTAX_ERR) description ("Argument is NOT a .xmlDocType.")
  end
  --trace i
  document = .xmlDocument~new
  document~implementation = self
  document~prefix = prefix
  document~localName = localName
  document~namespaceURI = namespaceURI
  if docType<>.nil then do
    document~appendChild(docType)
    docType~ownerDocument = document
  end
  document~docType = docType
  --if namespaceURI<>.nil | qualifiedName<>.nil | docType<>.nil then do
  --  documentElement = document~createElement(qualifiedName)
  --  document~appendChild(documentElement)
  --  document~documentElement = documentElement
  --end
return document
/**
  Creates an empty DocumentType node. Entity declarations and notations are not made available. Entity reference
  expansions and default attribute additions do not occur.
  @param qualifiedName - The qualified name for to new document (e.g. 'gpx')
  @param publicId - The public identifier of this document type (e.g. '-//W3C//DTD XHTML 1.0 Strict//EN')
  @param systemId - The system identifier for the document type (e.g. 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd')
  @return node - An empty xmlDocumentType node.
*/

::method createDocumentType
  use strict arg qualifiedName, publicId, systemId
  raise syntax 93.900 array (isUnderConstruction(.context~name))
return
/**
  This method returns a specialized object which implements the specialized APIs of the specified feature and version,
  as specified in DOM Features. The specialized object may also be obtained by using binding-specific casting methods
  but is not necessarily expected to, as discussed in Mixed DOM Implementations. This method also allow the
  implementation to provide specialized objects which do not support the DOMImplementation interface.
  @param feature - The name of the feature requested.
  @param version - This is the version number of the feature to test.
  @return object - Returns a xmlDomObject or <b>.nil</b>.

  The returned xmlDomObject which implements the specialized APIs of the specified feature and version, it is
  <b>.nil</b> if there is no object which implements interfaces associated with that feature.

  <b><u>NOTE</u>,/b> - The DOM Level 3 Feature handler support is not currently implemented, therefore <i>getFeature</i>
  will always return <b>.nil</b>.
*/

::method getFeature
  use strict arg feature, version
  featureHandler = .nil
  versions = self~features[feature~lower]
  if versions<>.nil then do
    featureHandler = versions~index(feature)
  end
return featureHandler
/**
  Test if the DOM implementation implements a specific feature and version, as specified in DOM Features.
  @param feature - The name of the feature requested.
  @param version='' - This is the (optional) version number of the feature to test.
  @return boolean - Returns <b>.true</b> if the feature is implemented in the specified version, <b>.false</b>
  otherwise.
*/

::method hasFeature
  use strict arg feature, version=''
  versions = self~features[feature~lower]
  if versions<>.nil then do
    featureSupported = versions~index[feature~lower]
  end
return featureSupported<>.nil
/* :end */

/* CLASS: domBuilder */
/**

*/

::class dom1Builder public
/**
  The DOM (Level 1) implementation for building a DOM (Level 1)
 
  <b><u>NOTE</u></b> - The <i>domImplementation</i> is currently only used for it's <i>createDocument</i> method.
*/

::attribute dom private
/**
  The DOM document resulting from the XML Parser and DOM (Level 1) builder.
*/

::attribute doc get
::attribute doc set private
/**
  The stack of nested elements with childnodes.
 
  <b><u>NOTE</u></b> - nodeStack[1] represents the current active Element node.
*/

::attribute nodeStack private
/**
  Represents the current active Attr node
*/

::attribute attrNode private
/**
  Flag to indicate if the convenience attribute <i>documentElement</i> must be set or not.
*/

::attribute haveDocumentElement private
/**
  DOM Builder instance constructor
 
  <b><u>NOTE</u></b> - domImplementation was introduced in DOM Level2, it is used here only to create a document with no
  namespace and doctype.
*/

::method init
  self~dom = .xmlDomImplementation~new
  self~nodeStack = .queue~new
  self~haveDocumentElement = .false
return
/**
  The method invoker wants to start a new DOM document. A DOM Level1 document is created with the help of the DOM Level2
  DomImplemetation interface.
  @param content=.nil - Currently ignored, if present
*/

::method startDocument
  use strict arg content=.nil
  self~doc = self~dom~createDocument()
  self~nodeStack~push(self~doc)
return
/**
  Signals the end of DOM building process. At this stage the stack with active nodes should only have the document node
  as content.
*/

::method endDocument
  use strict arg
  -- stack should have just 1 item now
  if self~nodeStack~items<>1 then do
    raise syntax 93.900 array ("Error in XML input")
  end
  docNode = self~nodeStack~pull
return
/**
  Start a new element node.
  @param tagName - the name of the tag for this element node.
 
  If this is the first element created, the convenience attribute <i>documentElement</i> is set. The new element node is
  made the currently active one after being appended as a childnode of the parentnode.
*/

::method startElement
  use strict arg tagName
  elementNode = self~doc~createElement(tagName)
  if (self~haveDocumentElement==.false) then do
    self~doc~documentElement = elementNode
    self~haveDocumentElement = .true
  end
  self~nodeStack[1]~appendChild(elementNode)
  self~nodeStack~push(elementNode)
return
/**
  Finish the currently active element node.
  @param tagName - the name of the tag of the element being ended.
  @raises syntax 93.900 - Mismatch in start and ending tag.

  The previous elemnt node is made the active node, by removing this active node from the stack.
*/

::method endElement
  use strict arg tagName
  if self~nodeStack[1]~tagName<>tagName then do
    raise syntax 93.900 array ("Mismatch in start and ending tag")
  end
  -- get rid of processed node
  processedNode = self~nodeStack~pull
return
/**
  Handle an attribute's name
  @param attrName - the name part of the current element's attribute.
 
  This will create the Attr node for the attribute, no name space checking is done.
*/

::method nameAttr
  use strict arg attrName
  self~attrNode = self~doc~createAttribute(attrName)
return
/**
  Handle an attibute's value.
  @param attrValue - the value part of the current element's attribute.
 
  Appends this Attr node to the (perhaps empty) list of attributes. Also sets the Attr's node ownerelement.
*/

::method valueAttr
  use strict arg attrValue
  self~attrNode~nodeValue = attrValue
  self~attrNode~ownerElement = self~nodeStack[1]
  self~nodeStack[1]~setAttributeNode(self~attrNode)
return
/**
  Handle a Processing Instruction (PI).
  @param target - The target for the PI (e.g. 'xml' in '&lt;?xml version=.......?&gt;'.
  @param data - The information following the target as a string.
 
  The PInode is appended as a childnode for the currently active element node.
*/

::method havePI
  use strict arg target, data
  piNode = self~doc~createProcessingInstruction(target,data)
  self~nodeStack[1]~appendChild(piNode)
return  
/**
  Handle a Comment.
  @param comment - The string representing the comment
 
  No validity checking is done on the comment's content. The comment node is appended as a childnode for the active
  element node.
*/

::method haveComment
  use strict arg comment
  cmtNode = self~doc~createComment(comment)
  self~nodeStack[1]~appendChild(cmtNode)
return
/**
  Handle a Text node.
  @param text - the (inner) text of the node with entities resolved.
 
  The text node is appended as a childnode for the active element node.
*/

::method haveText
  use strict arg text
  txtNode = self~doc~createTextNode(text)
  self~nodeStack[1]~appendChild(txtNode)
return
/**
  Handle CDATA.
  @param cdata - The raw (unparsed) text for the CDATASection node.
 
  The CDATA node is appended as a childnode for the currently active element node
*/

::method haveCDATA
  use strict arg cdata
  cdataNode = self~doc~createCDATASection(cdata)
  self~nodeStack[1]~appendChild(cdataNode)
return
/* :end */

/* CLASS: xmlParser */
/**
  A non-validating simple XML parser. The parser recognizes the following:
  <ul>
  <li>Document start, when a XML file has been stringified and when the <i>parseSting</i> method has accepted the
  string.</li>
  <li>XML Comments, no test for not allowed comment nesting.</li>
  <li>Removable White Space is removed when encountered.</li>
  <li>Processing Instructions (PI's), split into target (e.g. xml in &lt;?xml version....?&gt;) and data.</li>
  <li>CDATA with it's raw unparsed text.</li>
  <li>Attribute only tags, with their parsed, but without namespace checking, attributes.</li>
  <li>Tags with atributes as above and their parsed (i.e. resolved entities) text content.</li>
  <li>Document end, when the end of the XML string has been reached.</li>
  </ul>
*/

::class xmlParser public
/**
  The DOM builder given as parameter at instance creation time
*/

::attribute domBuilder private
/**
  The xmlParser constructor.
  @param domBuilder - the DOM builder the parser should use
*/

::method init
  use strict arg domBuilder
  self~domBuilder = domBuilder
return
/**
  Converts a XML file or stream into a string and passes the string on to <i>parseString</i>.
  @param xmlStream - An object with a <i>supplier</i> method
  @return domDocument - the DOM created from the input stream or <b>.nil</b> in case of errors
*/

::method parseStream
  use strict arg xmlStream
  s = xmlStream~supplier
  xml = ''
  do while s~available
    xml ||= s~item || .endofline
    s~next
  end
return self~parseString(xml)
/**
  Analyzes the input string for situations the DOM builder wants to know about and invokes the appropiate methods for
  the builder to handle the situations.
  @param xml - the XML string to be parsed.
  @return domDocument - the DOM created by the builder or <b>.nil</b> in case of errors.
*/
 
::method parseString
  use strict arg xml
  self~domBuilder~startDocument
  do while (xml~pos('>')>0)
    tagEnd = xml~pos('>')
    -- check comment first cause it can appear anywhere
    cmtStart = xml~pos("<!--")
    if (cmtStart>0 & cmtStart<tagEnd) then do
      -- there may be (element) content in front of the comment
      parse var xml text "<!--" comment "-->" +3 xml
      -- repair comment xml
      tag = "<!--"comment"-->"
    end
    else do
      -- there could be (element) content in front of the tag(= '<')
      parse var xml text '<' tagContent '>' +1 xml
      -- repair tag xml
      tag = '<'tagContent'>'
    end
    -- if there is text it is the content for an element
    if (text<>'') then do
      -- if it is not ignorable whitespace
      if self~removeIgnorableWhiteSpace(text)<>''
        then self~domBuilder~haveText(self~resolveEntities(text))
        else text = ''
    end
    -- now handle the tag
    select
      when (tag~pos("<!--")>0) then do
        -- this is a comment
        parse var tag "<!--" comment "-->"
        self~domBuilder~haveComment(comment)
      end
      when (tag~pos("<?")>0) then do
        -- this is a processing instruction (e.g. &lt;?xml version=....?&gt;)
        parse var tag "<?" target data "?>"
        self~domBuilder~havePI(target, data)
      end
      when (tag~pos("<![CDATA[")>0) then do
        -- there is unparsed (raw) text
        parse var tag "<![CDATA[" rawText "]]>"
        self~domBuilder~haveCDATA(rawText)
      end
      when (tag~pos("/>")>0) then do
        -- there is a complete element
        parse var tag '<' tagName attributes "/>"
        self~domBuilder~startElement(tagName)
        -- handle attributes if any
        self~parseAttributes(self~removeIgnorableWhiteSpace(attributes))
        self~domBuilder~endElement(tagName)
      end
      when (tag~pos("</")>0) then do
        -- this is an element ending tag
        parse var tag "</" tagName '>'
        self~domBuilder~endElement(tagName)
      end
      otherwise do
        -- must be a element starting tag
        parse var tag '<' tagName attributes '>'
        self~domBuilder~startElement(tagName)
        -- handle attributes if any
        self~parseAttributes(self~removeIgnorableWhiteSpace(attributes))
      end
    end
  end
  self~domBuilder~endDocument
return self~domBuilder~doc
/**
  Processes the attributes specified within an element tag and passes them on the the builder.
  @param attributes - the string containing all the attributes specifications within an element tag.
*/

::method parseAttributes private
  use strict arg attributes
  attributes = attributes~strip
  do while (attributes<>'')
    parse var attributes attrName '=' rest  
    aposPos = rest~pos("'")
    quotPos = rest~pos('"')
    select
      when (quotPos=0) then delimiter = "'"
      when (aposPos=0) then delimiter = '"'
      when (aposPos<quotPos) then delimiter = "'"
      otherwise delimiter = '"'
    end
    parse var rest (delimiter) attrValue (delimiter) attributes
    attributes = attributes~strip
    self~domBuilder~nameAttr(attrName)
    self~domBuilder~valueAttr(self~resolveEntities(attrValue))
  end
return
/**
  Returns a string with whitespace removed.
  @param text - A string with embedded whitespace, perhaps
  @return text - The string with whitespace removed, if there was any.
*/

::method removeIgnorableWhiteSpace
  use strict arg text
return text~changeStr('09'x,'')~changeStr('0a'x,'')~changeStr('0d'x,'')~strip
/**
  Transforms (the standard) entities to their character representation.
  @param text - A string possibly with entities.
  @return text - The string with entities resolved if any.
 
  <b><u>NOTE</u></b> - As DOCTYPE is not supported (yet) no DOCTYPE defined entities are handled.
*/

::method resolveEntities private
  use strict arg text
  text = text~changeStr("&lt;",'<')
  text = text~changeStr("&gt;",'>')
  text = text~changeStr("&amp;",'&')
  text = text~changeStr("&apos;","'")
  text = text~changeStr("&quot;",'"')
return text
/* :end */
 




 
If you feel inclined to make corrections, suggestions etc., please mail me any.
All content © Ruurd Idenburg, 2007–2018, except where marked otherwise. All rights reserved. This page is primarily for non-commercial use only. The Idenburg website records no personal information and sets no ‘cookies’. This site is hosted on a VPS(Virtual Private System) rented from CloudVPS, a Dutch company, falling under Dutch (privacy) laws.

This page updated on Thu, 28 Apr 2016 14:59:56 +0200 by Ruurd Idenburg.