./code/xmlDOM.cls/var/www/html/ooRexx/wip
/**
**********************************************************************
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
.nil 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
.nil 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
.nil, 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
xmlNode class is the base class for real nodetypes such as
elementNode and
textNode.
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
null
*/
::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
/**
instances 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
use strict arg 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
NamedNodeMap representing the attributes in an
elementNode.
*/
::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
nodeList 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.
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
.nil.
*/
::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.
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
.nil.
*/
::attribute namespaceURI get
::attribute namespaceURI set
/**
*/
::attribute nextSibling
/**
The following nodeName values are valid for a particular type of node:
- Attr - xmlAttr~name (i.e. the name part of the name-value attribute pair).
- CDATASection - literal "#cdata-section".
- Comment - literal "#comment".
- Document - literal "#document".
- DocumentFragment - literal "#document-fragment".
- DocumentType - xmlDocumentType~name.
- Element - xmlElement~tagName.
- Entity - xmlEntity name (e.g. ?????).
- EntityReference - name of entity referenced (e.g. ?????).
- Notation - notation name (e.g. ?????).
- ProcessingInstruction - xmlProcessingInstruction~target.
- Text - literal "#text".
*/
::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:
- Attr - xmlAttr~value (i.e. the value part of the name-value attribute pair).
- CDATASection - xmlCharacterData~data (i.e. the content of the CDATA Section).
- Comment - xmlCharacterData.data (i.e. the content of the comment).
- Document - .nil
- DocumentFragment - .nil
- DocumentType - .nil
- Element - .nil
- Entity - .nil
- EntityReference - .nil
- Notation - .nil
- ProcessingInstruction - xmlProcessingInstruction~data
- Text - xmlCharacter~data (i.e. the content of the text node).
*/
::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
.nil.
*/
::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
.nil.
*/
::attribute parentNode get
::attribute parentNode set
/**
The namespace prefix of this node, or null if it is unspecified. When it is defined to be
.nil, 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
.nil.
*/
::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
child to the end of the list of children of this node. If the
child is already in the
tree, it is first removed.
@param child - the node to be appended.
@return node - the node appended
If
child 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
.nil) 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
.true or
.false (= default)
@return node - the duplicated node
If
deep is
.true, recursively clone the subtree under the specified node; if
.false, 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
deep
clone. This includes text contained in the xmlElement since the text is contained in a child xmlText node.
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
deep clone or not.
Cloning an xmlEntityReference automatically constructs its subtree if a corresponding xmlEntity is available, no
matter whether this is a
deep clone or not.
Cloning any other type of node simply returns a copy of this node.
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
.nil
*/
::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
.nil
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.
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
setUserData with the same key.
@param key - The key the object is associated to.
@return userdata - the DOMUserData associated, or
.nil if there was none
*/
::method getUserData
use strict arg key
raise syntax 93.963
exit
/**
@return boolean -
.true if this node has any attributes,
.false otherwise
*/
::method hasAttributes
use strict arg
return .false
/**
@return boolean -
.true if this node has any children,
.false otherwise
*/
::method hasChildNodes
use strict arg
hasChildren = .false
if (self~children~items>0) then hasChildren = .true
return hasChildren
/**
Inserts the node
child before the existing child node
where.
@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
where is .nil, insert
child at the end of the list of children.
If
child is a xmlDocumentFragment object, all of its children are inserted, in the same order, before
where.
If the
child is already in the tree, it is first removed.
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
namespaceURI is the default namespace or not.
@param uri - The
namespaceURI to look for
@return boolean -
.true if default,
.false 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 -
.true if equal,
.false 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.
Two nodes are equal if and only if the following conditions are satisfied:
- The two nodes are of the same type.
- 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.
- 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.
-
- 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.
Note that normalization can affect equality; to avoid this, nodes should be normalized before being compared.
For two xmlDocumentType nodes to be equal, the following conditions must also be satisfied:
- The following string attributes are equal: publicId, systemId, internalSubset.
- The entities xmlNamedNodeMaps are equal.
- The notations NamedNodeMaps are equal.
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 -
.true if the nodes are the same,
.false 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 -
.true if supported on this node,
.false 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
.nil.
*/
::method lastChild
use strict arg
child = .nil
if (self~children~items>0) then child = self~children[self~children~items]
return child
/**
Look up the
namespaceURI associated to the given prefix, starting from this node.
See Namespace URI Lookup for details on the algorithm used by this method.
@param prefix - The prefix to look for.
@return aString - The associated
namespaceURI or
.nil if none is found.
If
prefix is null, the method will return the default
namespaceURI if any.
*/
::method lookupNamespaceURI
use strict arg prefix
raise syntax 93.963
exit
/**
Look up the prefix associated to the given
namespaceURI, starting from this node.
@param uri - A string specifying the
namespaceURI to look for
@return aString - An associated namespace prefix or
.nil 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.
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
.nil.
*/
/*::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
If the parameter "normalize-characters" of the DOMConfiguration object attached to the xmlNode~ownerDocument is
.true, this method will also fully normalize the characters of the xmlText nodes.
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 .nil
*/
/**
::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 aNode 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 old with new in the list of children, and returns the old 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 new is a xmlDocumentFragment object, old is replaced by all of the
xmlDocumentFragment children, which are inserted in the same order. If the new
is already in the tree, it is first removed.
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 .nil.
*/
::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 .nil, 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 .nil.
@param index - Index into this map.
@return node - The node at the indexth position in the map, or .nil 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 .nil 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 nodeName 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 nodeName attribute.
@return node - The replaced xmlNode or .nil 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 namespaceURI and localName.
@return node - The replaced xmlNode or .nil 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 parentNode,
previousSibling, and nextSibling have a .nil 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 nodeValue 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 normalizeDocument, 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
getElementById method. The implementation could use several ways to determine if an attribute node is known to
contain an identifier:
- If validation occurred using an XML Schema [XML Schema Part 1] while loading the document or while invoking the
xmlDocument normalizeDocument 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].
- If validation occurred using a DTD while loading the document or while invoking the xmlDocument
normalizeDocument 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].
- From the use of the xmlElement setIdAttribute, setIdAttributeNS, or setIdAttributeNode, i.e.
it is an user-determined ID attribute;
- 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.
If validation occurred while invoking the xmlDocument normalizeDocument 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 schemaTypeInfo attribute contains an ID type, isId will always return
.true.
*/
::attribute isId get
::attribute isId set
/**
Returns the name of this attribute. If the xmlNode localName is different from .nil, this attribute is a
qualified name.
*/
::method name
return self~nodeName
/**
The xmlElement node this attribute is attached to or .nil 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 normalizeDocument method,
schemaTypeInfo may not be reliable if the node was moved.
*/
::attribute schemaTypeInfo get
::attribute schemaTypeInfo set
/**
.true 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 .true. The implementation may handle attributes with default values from other schemas
similarly but applications should use the xmlDocument normalizeDocument 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 getAttribute 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 setAttribute
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, data and length 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, data and length 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 ''. 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 normalize 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 normalizeDocument 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 wholeText 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:
- .nil, when the replacement text is the empty string;
- the current node, except when the current node is read-only;
- a new xmlText node of the same type (xmlText or xmlCDATASection) as the current node inserted at the location of
the replacement.
@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 data 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 normalize 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 .nil instead of an instance of this interface.
xmlDocumentType is a subclass (precisation) of xmlNode, and adds the following attributes:
- 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 .nil if the information is not provided by the parser, or if no entities are
defined.
- 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 .nil.
- name - The name of the root element as given in the DOCTYPE declaration, if present.
- 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
.nil if the information is not provided by the parser, or if no notations are defined.
- publicId - The public identifier for the external subset of the document type definition. This will be a string
or .nil.
- systemId - The system identifier for the external subset of the document type definition. This will be a URI as a
string, or .nil.
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 .nil if there is none. This does not contain the delimiting square
brackets.
NOTE, - 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 localName 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 .nil 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 .nil 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 - .true if an attribute with the given name is specified on this element or has a default
value, .false 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 - .true if an attribute with the given local name and namespace URI is specified or has a
default value on this element, .false otherwise.
Per [XML Namespaces], applications must use the value .nil 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 .nil 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 setAttributeNode 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 setAttributeNodeNS or
setAttributeNode 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 .nil 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
.nil 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
.nil is returned.
@raises user domExcep[tion - WRONG_DOCUMENT_ERR
@raises user domException - INUSE_ATTRIBUTE_ERR
Per [XML Namespaces], applications must use the value .nil 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 isId meyhod and the behavior of the xmlDocument getElementById method,
but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
schemaTypeInfo property of the specified xmlAttr node.
Use the value .false 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 isId meyhod and the behavior of the xmlDocument getElementById method,
but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
schemaTypeInfo property of the specified xmlAttr node.
Use the value .false 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 isId meyhod and the behavior of the xmlDocument getElementById method,
but does not change any schema that may be in use, in particular this does not affect the the xmlAttr
schemaTypeInfo property of the specified xmlAttr node.
Use the value .false 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 .nil. 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
insertBefore, or xmlNode's replaceChild.
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 .nil if undefined or if the Document was created using xmlDOMImplementation
createDocument. No lexical checking is performed when setting this attribute; this could result in a
.nil value returned when using xmlNode baseURI.
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 baseURI.
*/
::attribute documentURI
/**
The configuration used when xmlDocument normalizeDocument 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 public and is set to private once set
turning the attribute into a read-only.
*/
::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 .false, 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 normalizeDocument. 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 .nil 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 .false
when unspecified.
Note: No verification is done on the value when setting this attribute. Applications should use xmlDocument
normalizeDocument 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 normalizeDocument in order to check for invalid characters
in the Nodes that are already part of this Document.
DOM applications may use the xmlDOMImplementation hasFeature(feature, version) 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 xmlVersion.
@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 createDocument 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 importNode which create a copy of the source node instead of moving it). When it fails,
applications should use xmlDocument importNode 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.
- 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.
- DOCUMENT_FRAGMENT_NODE - The descendants of the source node are recursively adopted.
- DOCUMENT_NODE - Document nodes cannot be adopted.
- DOCUMENT_TYPE_NODE - DocumentType nodes cannot be adopted.
- 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.
- ENTITY_NODE - Entity nodes cannot be adopted.
- 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.
- NOTATION_NODE - Notation nodes cannot be adopted.
- PROCESSING_INSTRUCTION_NODE, TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE - These nodes can all be adopted. No
specifics.
Note: Since it does not create new nodes unlike the xmlDocument importNode method, this method does not raise
an INVALID_CHARACTER_ERR exception, and applications should use the xmlDocument normalizeDocument 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 .nil 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
setAttributeNode method.
The new xmlAttr has the nodeName attribute set to name, and localName, prefix, and
namespaceURI 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
xmlVersion attribute.
To create an attribute with a qualified name and namespace URI, use the createAttributeNS 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:
- nodeName - The specified qualifiedName.
- namespaceURI - The specified namespaceURI.
- prefix - prefix, extracted from qualifiedName, or null if there is no prefix.
- localName - name, extracted from the specified qualifiedName.
- name - The specified qualifiedName.
- nodeValue - An empty string (i.e. "").
@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 tagName, 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:
- nodeName - The specified qualifiedName.
- namespaceURI - The specified namespaceURI.
- prefix - prefix, extracted from qualifiedName, or null if there is no prefix.
- localName - name, extracted from the specified qualifiedName.
- tagName - The specified qualifiedName.
@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.
NOTE - 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 .nil 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.
- 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.
NOTE - The deep parameter has no effect on xmlAttr nodes; they always carry their children with them
when imported.
- 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.
- DOCUMENT_NODE - Document nodes cannot be imported.
- DOCUMENT_TYPE_NODE - DocumentType nodes cannot be imported.
- 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.
- 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.
- 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.
- NOTATION_NODE - Notation nodes can be imported, however they are read-only
- PROCESSING_INSTRUCTION_NODE - The imported node copies its target and data from those of the source node.
- 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.
@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 normalize.
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:
- a user data event NODE_RENAMED is fired
- 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.
<\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 .nil.
@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 .
@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 .nil, the returned xmlDocument is empty with no document
element.
When docType is not .nil, its ownerDocument 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 namespaceURI.")
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 .nil.
The returned xmlDomObject which implements the specialized APIs of the specified feature and version, it is
.nil if there is no object which implements interfaces associated with that feature.
NOTE,/b> - The DOM Level 3 Feature handler support is not currently implemented, therefore getFeature
will always return .nil.
*/
::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 .true if the feature is implemented in the specified version, .false
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)
NOTE - The domImplementation is currently only used for it's createDocument 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.
NOTE - 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 documentElement must be set or not.
*/
::attribute haveDocumentElement private
/**
DOM Builder instance constructor
NOTE - 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 documentElement 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 '<?xml version=.......?>'.
@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:
- Document start, when a XML file has been stringified and when the parseSting method has accepted the
string.
- XML Comments, no test for not allowed comment nesting.
- Removable White Space is removed when encountered.
- Processing Instructions (PI's), split into target (e.g. xml in <?xml version....?>) and data.
- CDATA with it's raw unparsed text.
- Attribute only tags, with their parsed, but without namespace checking, attributes.
- Tags with atributes as above and their parsed (i.e. resolved entities) text content.
- Document end, when the end of the XML string has been reached.
*/
::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 parseString.
@param xmlStream - An object with a supplier method
@return domDocument - the DOM created from the input stream or .nil 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 .nil 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("" +3 xml
-- repair comment xml
tag = ""
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(""
self~domBuilder~haveComment(comment)
end
when (tag~pos("")>0) then do
-- this is a processing instruction (e.g. <?xml version=....?>)
parse var tag "" target data "?>"
self~domBuilder~havePI(target, data)
end
when (tag~pos("0) then do
-- there is unparsed (raw) text
parse var tag ""
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 (aposPosNOTE - As DOCTYPE is not supported (yet) no DOCTYPE defined entities are handled.
*/
::method resolveEntities private
use strict arg text
text = text~changeStr("<",'<')
text = text~changeStr(">",'>')
text = text~changeStr("&",'&')
text = text~changeStr("'","'")
text = text~changeStr(""",'"')
return text
/* :end */