/** * Changes for Persistent DOM running with ozone are * Copyright 1999 by softwarebuero m&b (SMB). All rights reserved. */ package org.ozoneDB.xml.dom; import java.util.*; import java.io.*; import org.ozoneDB.*; import org.ozoneDB.xml.dom.iterator.*; import org.w3c.dom.*; public abstract class NodeImpl extends OzoneObject implements NodeProxy, Externalizable { final static long serialVersionUID = 1; /** * Abstract method must be implemented by each node class. * * @see org.w3c.dom.Node#getNodeType */ public abstract short getNodeType(); /** * Returns the name of the node, set from the constructor. Some derived classes * do not have the notion of a name, and will return the same name each time. * They should do so by setting the default name (e.g. "#comment") * in the constructor. This value is never null. * * @see org.w3c.dom.Node#getNodeName */ public final String getNodeName() { return _nodeName; } /** */ public final void setNodeName (String nodeName) { _nodeName = nodeName; } /** * Returns the value of the node. Depending on the node type, this value * is either the node value (e.g. the text in {@link org.w3c.dom.Text}), * or always null is node has no notion of a value (e.g. {@link * org.w3c.dom.Element}). For complete list of which node types will return * what, see {@link #setNodeValue}. * * @return Value of node, null if node has no value */ public final String getNodeValue() { return _nodeValue; } /** * Changes the value of the node. Not all node types support the notion of * a value. If the value is not supported by a particular node type, it will * throw an exception when calling this method. The following table specifies * which node types support values: *
     * Element                  Not supported
     * Attr                     Value supported
     * Text                     Value supported
     * CDATASection             Value supported
     * EntityReference          Not supported
     * Entity                   Not supported
     * ProcessingInstruction    Value supported
     * Comment                  Value supported
     * Document                 Not supported
     * DocumentType             Not supported
     * DocumentFragment         Not supported
     * Notation                 Not supported
     * 
* For most node types, if the value is set to null, {@link #getNodeValue} * will return an empty string instead. * * @param value New value of node * @throws org.w3c.dom.DOMExceptionImpl NO_MODIFICATION_ALLOWED_ERR * Node is read-only and cannot be modified * @throws org.w3c.dom.DOMExceptionImpl NO_DATA_ALLOWED_ERR * This node does not support a value */ public void setNodeValue( String value ) { if ( isReadOnly() ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); _nodeValue = value == null ? "" : value; } /** * Returns the parent node of this node. Node may not necessarily have a * parent node. If node has been created but not added to any other node, * it will be parentless. The {@link org.w3c.dom.Document} node is always * parentless. * * @return Parent node of this node */ public Node getParentNode() { return _parent; } /** */ public void setParentNode (Node newParent) { _parent = (NodeProxy)newParent; } /** * Called to notify all the iterators created from this node that a * child of this node has been removed. Iterators that point at this * child might choose to select another child to point to. This method * is called before the child is removed. *

* The removed node is a direct child of this node. Affected iterators * are those that point at the document tree directly below this node, * or the tree below one of its parents. Other iterators are not affected * by the change. This method also performs a notification on all the * parents of this node. * * @param removedChild The child node being removed */ protected void notifyIterators( Node removedChild ) { /* FIXME NodeProxy node; int i; node = this; while ( node != null ) { if ( node._iterators != null ) for ( i = node._iterators.length ; i -- > 0 ; ) ( (NodeIteratorListener) node._iterators[ i ] ).removeNode( removedChild ); node = (NodeProxy) node.getParentNode(); } */ } /** * Returns a {@link org.w3c.dom.NodeList} object that can be used to traverse * this node's children. The node list is live, so every change to this node * is reflected in it. *

* If children are not supported by the derived class, an exception is thrown. * * @return {@link org.w3c.dom.NodeList} on this node * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR Childern not supported * by this node type * @see org.w3c.dom.NodeList * @see NodeListImpl */ public NodeList getChildNodes() { // Throw exception if children not supported by derived class. if (!supportsChildern ()) throw new DOMExceptionImpl (DOMException.HIERARCHY_REQUEST_ERR, "No childern supported by this node type."); return (NodeList) new org.ozoneDB.xml.dom.NodeListImpl (this); } /** * Returns the first child of the node. If node has no children, returns null. * * @return First child or null */ public final Node getFirstChild() { return _firstChild; } /** * Returns the last child of the node. If node has no children, returns null. * * @return Last child or null */ public final Node getLastChild() { return _lastChild; } /** * Returns the previous sibling of this node. If node has no previous siblings, * returns null. * * @return Previous sibling or null */ public Node getPreviousSibling() { return _prevNode; } /** */ public void setPreviousSibling (Node prevNode) { _prevNode = (NodeProxy)prevNode; } /** * Returns the next sibling of this node. If node has no next siblings, * returns null. * * @return Next sibling or null */ public Node getNextSibling() { return _nextNode; } /** */ public void setNextSibling (Node nextNode) { _nextNode = (NodeProxy)nextNode; } /** * Return attributes of node. Returns null unless node is of type {@link * org.w3c.dom.Element}, in which case the returned {@link * org.w3c.dom.NamedNodeMap} will provide access to all the element's * attributes. * * @return Attributes of node or null */ public NamedNodeMap getAttributes() { return null; } public final Document getOwnerDocument() { if ( _ownerDocument != this ) return _ownerDocument; else return null; } /** * Return true if there are any childern to this node. Less intensive than * calling {#link getChildNodes}. * * @return True if node has any children */ public final boolean hasChildNodes() { return ( _firstChild != null ); } /** * Insert newChild as the last child of this parent. *

* If newChild is null, newChild does not belong to this DOM, * or childern are not supported by this node type, an exception is thrown. *

* newChild is removed from its original parent before adding to this * parent. If newChild is a {@link org.w3c.dom.DocumentFragment}, all * its children are inserted one by one into this parent. * * @param newChild The new child to add * @return The newly inserted child * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR * Node is read-only and cannot be modified * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR * Children are not supported by this node type, or newChild is not * a compatible type for this node * @see #castNewChild * @see #castOldChild */ public final synchronized Node appendChild (Node newChild) { // Node arguments must be casted to NodeEx in order to operate on them. NodeProxy newChildX; // Make sure the node is not read-only. // Throw exception if children not supported by derived class. if ( isReadOnly() ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); if ( ! supportsChildern() ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "No childern supported by this node type." ); // Cast newChild to NodeImpl and make sure it can be inserted to this node. newChildX = (NodeProxy)castNewChild (newChild); // We're going to mess with this child node, so make sure no other thread // is touching it synchronized ( newChild ) { // If the newChild is already a child or some node, remove it first // before becoming child of this node. Make sure that parent is not // read-only. if ( newChildX.getParentNode () != null) { if ( ((NodeProxy)(newChildX.getParentNode())).isReadOnly () ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); newChildX.getParentNode().removeChild (newChildX); } // Special case: newChild is a DocumentFragment and instead of adding // itself, all of its childs are added one by one. if ( newChildX instanceof DocumentFragment ) { NodeProxy nextChild; newChildX = (NodeProxy)newChildX.getFirstChild (); while ( newChildX != null ) { nextChild = (NodeProxy)newChildX.getNextSibling (); appendChild ( newChildX ); newChildX = nextChild; } return newChild; } // Node becomes child of this parent and part of this document. // Note that this code comes after the test for a DocumentFragment. // A fragment does not become part of this node, only its children. // The fragment becomes parent-less and child-less. // newChildX._parent = this; newChildX.setParentNode ( this ); if ( _ownerDocument != null ) newChildX.setOwnerDocument( _ownerDocument ); // If the list has no end (it is empty) then newChild is added as the // only child in it. if ( _lastChild == null ) { _lastChild = newChildX; _firstChild = newChildX; newChildX.setPreviousSibling (null); newChildX.setNextSibling (null); } // newChild becomes the new end of the list, adjusting the previous // last child. else { _lastChild.setNextSibling (newChildX); newChildX.setPreviousSibling (_lastChild); newChildX.setNextSibling (null); _lastChild = newChildX; } // Keep this count accurate at all times. ++ _childsCount; } return newChild; } /** * Remove oldChild from this parent. If oldChild is not * a direct child of this parent, or childern are not supported by this node * type, an exception is thrown. * * @param oldChild The child to remove * @return The removed child * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR * Node is read-only and cannot be modified * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR * Children are not supported by this node type * @throws org.w3c.dom.DOMException NOT_FOUND_ERR * oldChild is not a direct child of this node * @see #castOldChild */ public final synchronized Node removeChild( Node oldChild ) throws DOMException { NodeProxy oldChildX; int i; // Make sure the node is not read-only. // Throw exception if children not supported by derived class. if ( isReadOnly() ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); if ( ! supportsChildern() ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "No childern supported by this node type." ); // Cast refChild to NodeImpl, making sure it is a child of this node. oldChildX = (NodeProxy)castOldChild( oldChild ); // We're going to mess with this child node, so make sure no other thread // is touching it synchronized (oldChild) { // Need to tell all the iterators that might be observing the // child node that the child node is removed from the current // tree. The iterators will reflect the changed by selecting // a different child to point to. Interesting iterators are // those the observer the tree underneath this node and all its // parents. notifyIterators (oldChild); // Child becomes orphan. It is no longer first or last child of this // node. Removed from linked list. oldChildX.setParentNode (null); if ( _firstChild != null && _firstChild.equals( oldChildX ) ) _firstChild = (NodeProxy)oldChildX.getNextSibling (); if (_lastChild != null && _lastChild.equals( oldChildX ) ) _lastChild = (NodeProxy)oldChildX.getPreviousSibling (); if ( oldChildX.getPreviousSibling () != null ) ((NodeProxy)oldChildX.getPreviousSibling()).setNextSibling ( oldChildX.getNextSibling() ); if ( oldChildX.getNextSibling () != null ) ((NodeProxy)oldChildX.getNextSibling()).setPreviousSibling ( oldChildX.getPreviousSibling () ); oldChildX.setPreviousSibling (null); oldChildX.setNextSibling (null); // Keep this count accurate at all times. -- _childsCount; } return oldChild; } /** * Replace oldChild with newChild, adding the new child and * removing the old one. *

* If newChild does not belong to this DOM, oldChild is not * a direct child of this parent, or childern are not supported by this node * type, an exception is thrown. *

* newChild is removed from its original parent before adding to this * parent. If newChild is a {@link org.w3c.dom.DocumentFragment}, all * its children are inserted one by one into this parent. * * @param newChild The new child to add * @param oldChild The old child to take away * @return The old child * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR * Node is read-only and cannot be modified * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR * Children are not supported by this node type, or newChild is not * a compatible type for this node * @throws org.w3c.dom.DOMException NOT_FOUND_ERR * oldChild is not a direct child of this node * @see #castNewChild * @see #castOldChild */ public final synchronized Node replaceChild( Node newChild, Node oldChild ) throws DOMException { // Node arguments must be casted to NodeEx in order to operate on them. NodeProxy newChildX; NodeProxy oldChildX; // Make sure the node is not read-only. // Throw exception if children not supported by derived class. if ( isReadOnly() ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); if ( ! supportsChildern() ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "No childern supported by this node type." ); // Cast newChild to NodeImpl and make sure it can be inserted to this node. // Cast oldChild to NodeImpl, making sure it is a child of this node. if ( newChild != null ) newChildX = (NodeProxy)castNewChild( newChild ); oldChildX = (NodeProxy)castOldChild( oldChild ); // We're going to mess with this child node, so make sure no other thread // is touching it synchronized ( oldChild ) { if ( newChild != null ) { // .. or this synchronized ( newChild ) { // Lazy implementation adds newChild before oldChild and then takes // oldChild away. Might be a touch slowed, but is way more reliable. insertBefore ( newChild, oldChild ); removeChild ( oldChild ); } } else // The case of just removing the old child, when the new one // is null. removeChild ( oldChild ); } return oldChild; } /** * Insert newChild in this parent, before the existing child * refChild. If refChild is null, insert newChild * as the last child of this parent, akin to calling {@link #appendChild}. *

* If newChild is null, newChild does not belong to this DOM, * refChild is not a direct child of this node, or childern are not * supported by this node type, an exception is thrown. *

* newChild is removed from its original parent before adding to this * parent. If newChild is a {@link org.w3c.dom.DocumentFragment}, all * its children are inserted one by one into this parent. * * @param newChild The new child to add * @param refChild Insert new child before this child, or insert at the end * if this child is null * @return The newly inserted child * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR * Node is read-only and cannot be modified * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR * Children are not supported by this node type, or newChild is not * a compatible type for this node * @throws org.w3c.dom.DOMException NOT_FOUND_ERR * oldChild is not null and not a direct child of this node * @see #castNewChild * @see #castOldChild */ public final synchronized Node insertBefore( Node newChild, Node refChild ) throws DOMException { // Node arguments must be casted to NodeEx in order to operate on them. NodeProxy newChildX; NodeProxy refChildX; // Make sure the node is not read-only. // Throw exception if children not supported by derived class. if ( isReadOnly() ) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); if ( ! supportsChildern() ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "No childern supported by this node type." ); // If refChild is null, act as if appendChild was called. if ( refChild == null ) return appendChild ( newChild ); // Cast newChild to NodeImpl and make sure it can be inserted to this node. // Cast refChild to NodeImpl, making sure it is a child of this node. newChildX = (NodeProxy)castNewChild( newChild ); refChildX = (NodeProxy)castOldChild( refChild ); // We're going to mess with this child node, so make sure no other thread // is touching it synchronized ( newChild ) { // .. or this synchronized ( refChild ) { // If the newChild is already a child or some node, remove it first // before becoming child of this node. Make sure that parent is not // read-only. if ( newChildX.getParentNode () != null ) { if ( ((NodeProxy)newChildX.getParentNode()).isReadOnly ()) throw new DOMExceptionImpl( DOMException.NO_MODIFICATION_ALLOWED_ERR ); newChildX.getParentNode().removeChild ( newChildX ); } // Special case: newChild is a DocumentFragment and instead of // inserting itself, all of its childs are inserted one by one. if ( newChildX instanceof DocumentFragment ) { NodeProxy nextChild; newChildX = (NodeProxy)newChildX.getFirstChild (); while ( newChildX != null ) { nextChild = (NodeProxy)newChildX.getNextSibling (); insertBefore( newChildX, refChild ); newChildX = nextChild; } return newChild; } // Node becomes child of this parent and part of this document. // Note that this code comes after the test for a DocumentFragment. // A fragment does not become part of this node, only its children. // The fragment becomes parent-less and child-less. newChildX.setParentNode (this); newChildX.setOwnerDocument( _ownerDocument ); // If refChild is the first child, newChild becomes the first // child on the list. if ( _firstChild.equals( refChildX ) ) _firstChild = newChildX; // refChild is not the first child, so adjust the previous child // to point at newChild instead. if ( refChildX.getPreviousSibling () != null ) { newChildX.setPreviousSibling ( refChildX.getPreviousSibling() ); ((NodeProxy)refChildX.getPreviousSibling()).setNextSibling ( newChildX ); } // Adjust the refChild to point at this child and vice versa. refChildX.setPreviousSibling ( newChildX ); newChildX.setNextSibling ( refChildX ); // Keep this count accurate at all times. ++ _childsCount; } } return newChild; } /** * Checks whether newChild can be added to this node as a child, and * if so, performs a necessary cast. newChild cannot be null and must * belong to this DOM. It is impossible to transfer nodes between different * DOM implementations. *

* The following rules govern the allowed newChild types: *

* Any deviation will throw an exception. * * @param newChild New child node * @return newChild cast to type {@link NodeImpl} * @throws org.w3c.dom.DOMException HIERARCHY_REQUEST_ERR * newChild is null, does not belong to this DOM, or its node type * is not supported for this parent */ protected Node castNewChild (Node newChild) throws DOMException { if ( newChild == null ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "Child reference is null." ); // newChild must be Element, CDATASection, Text, Comment (all three // derived from CharacterData), DocumentFragment, EntityReference, // or ProcessingInstruction. if ( ! (newChild instanceof Node) ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "Child is not a compatible type for this node." ); if ( ! ( newChild instanceof Element || newChild instanceof CharacterData || newChild instanceof DocumentFragment || newChild instanceof EntityReference || newChild instanceof ProcessingInstruction ) ) throw new DOMExceptionImpl( DOMException.HIERARCHY_REQUEST_ERR, "Child is not a compatible type for this node." ); return (Node) newChild; } /** * Checks whether oldChild is a direct child of this node, and if so, * performs a necessary cast. oldChild cannot be null. * * @param oldChild Old child node * @return oldChild cast to type {@link NodeImpl} * @throws org.w3c.dom.DOMException NOT_FOUND_ERR * oldChild is null, or not a direct child of this node */ protected final Node castOldChild( Node oldChild ) throws DOMException { if ( oldChild == null || ! ( oldChild instanceof NodeProxy ) || !(( (OzoneProxy)oldChild.getParentNode ()).remoteID ().equals (container().id ())) ) throw new DOMExceptionImpl( DOMException.NOT_FOUND_ERR, "Not a direct child of this node." ); return (Node) oldChild; } /** * This clone method is called after a new node has been constructed to copy * the contents of this node into the new one. It clones in contents but not * in context, and guarantees that the cloned node will pass the equality * test (see {@link #equals}). *

* into must be a valid node of the exact same class as this one. * deep is true if deep cloning (includes all children nodes) is to * be performed. If deep is false, the clone might not pass the * equality test. *

* Derived classes override and call this method to add per-class variable * copying. This method is called by {@link #cloneNode} and the default * {@link java.lang.Object#clone} method. *

* Contents cloning duplicates the node's name and value, and its children. * It does not duplicate it's context, that is, the node's parent or sibling. * Initially a clone node has no parents or siblings. However, the node does * belong to the same document, since all nodes must belong to some document. * The cloned node is never read-only. * * @param into A node into which to duplicate this one * @param deep True if deep cloning is required */ public synchronized void cloneInto (NodeProxy into, boolean deep) { NodeProxy child; // Make sure no function messed up with the class types. if ( this.getClass() != into.getClass() ) throw new IllegalArgumentException( "Argument 'into' not same type as this node." ); // Duplicate node name and value. into.setNodeName (_nodeName); into.setNodeValue (_nodeValue); into.setOwnerDocument (_ownerDocument); if ( deep ) { child = (NodeProxy)getFirstChild(); while (child != null) { into.appendChild ((Node)child.cloneNode (true)); child = (NodeProxy)child.getNextSibling(); } } } public synchronized void setOwnerDocument( Document owner ) { Node node; if ( owner == null ) _ownerDocument = null; else { if ( ! ( owner instanceof DocumentProxy ) ) throw new IllegalArgumentException( "Argument 'owner' not of compatible DOM class." ); _ownerDocument = (DocumentProxy) owner; } node = getFirstChild (); while ( node != null ) { ( (NodeProxy) node ).setOwnerDocument( owner ); node = node.getNextSibling(); } } /** * Renders this node read only, preventing it's contents from being modified. * Attempts to modify the node's contents will throw an exception. The node's * children are also made read-only. */ public final synchronized void setReadOnly() { NodeProxy child; _readOnly = true; // Make all children read-only as well: this allows us to lock a branch // but, for example, move it to a different tree. child = (NodeProxy)getFirstChild(); while ( child != null ) { child.setReadOnly(); child = (NodeProxy)child.getNextSibling(); } } /** * Returns true if node is read-only and cannot be modified, or if node * belongs to a read-only document. * * @return True if node is read-only and cannot be modified * @see #setReadOnly */ public final boolean isReadOnly() { return _readOnly; } /** * Returns true if this node supports children. Other methods query this to * determine whether to properly support childern, return null or throw an * exception in response. The default method returns false. * * @return True if childern supported by this node type */ boolean supportsChildern() { return false; } /** * Returns the index-th child of this node. This method is used * exclusively by {@link NodeListImpl}. * * @param index Index of child to retrieve * @return The child node or null * @see NodeListImpl#item(int) */ public final synchronized Node getChild( int index ) { NodeProxy node; if ( index < 0 || index > _childsCount ) return null; node = (NodeProxy)getFirstChild(); while ( node != null && index > 0 ) { node = (NodeProxy)node.getNextSibling(); --index; } return node; } /** * Returns the number of children in this node. This method is used * exclusively by {@link NodeListImpl}. * * @return Number of childern in this node * @see NodeListImpl#getLength */ public final int getChildCount() { return _childsCount; } /** * Hidden constructor creates a new node. Only one constructor is supported, * although cloning is also supported. Owner document must be supplied except * for {@link DocumentImpl} in which case the document itself becomes its * owner. Name must be supplied, either dynamic or static (e.g. "#document#"). *

* If checkName is true, the supplied named is assumed to be a valid * XML name token, one that can contain any Unicode letter and digit, must * start with a letter, and may also contain hyphen, underscore, digit or colon. * * @param owner Document owner of this node, or null * @param name Name of node * @param value Initial value of node or null * @param checkName True if name is an XML name token * @throws org.w3c.dom.DOMException INVALID_CHARACTER_ERR * Node name cannot contain whitespaces or non-printable characters */ protected NodeImpl (DocumentImpl owner, String name, String value, boolean checkName) throws DOMException { init (owner, name, value, checkName); } protected NodeImpl () { } public final void init (DocumentProxy owner, String name, String value, boolean checkName) throws DOMException { char ch; int i; if ( name == null ) throw new NullPointerException( "Argument 'name' is null." ); _nodeName = name; _ownerDocument = owner; // Check the node name one character at a time to assure that no // illegal characters are used. Node name must conform to Name token // as defined in XML spec, including use of all Unicode letters and // digits. if (checkName && name.length () > 0) { ch = name.charAt( 0 ); if ( ! Character.isLetter( ch ) && ch != '_' && ch != ':' ) throw new DOMExceptionImpl( DOMException.INVALID_CHARACTER_ERR ); for ( i = 1 ; i < name.length() ; ++i ) { ch = name.charAt( 1 ); if ( ! Character.isLetterOrDigit( ch ) && ch != '_' && ch != ':' && ch != '-' && ch != '.' ) throw new DOMExceptionImpl (DOMException.INVALID_CHARACTER_ERR); } } if (value != null) setNodeValue (value); } /** * Element declaration node. Not part of the DOM, identifies an element * declaration node appearing in the DTD. */ public static final short ELEMENT_DECL_NODE = 13; /** * Attributes list declaration node. Not part of the DOM, identifies an * attributes list declaration node appearing in the DTD.. */ public static final short ATTLIST_DECL_NODE = 14; /** * Parameter entity declaration node. Not part of the DOM, identifies an * internal or external parameter entity declaration node appearing in the * DTD (see {@link org.openxml.dom.ParamEntity}). */ public static final short PARAM_ENTITY_NODE = 15; /** * This node ia part of a double-linked list that belongs to its parent. * This reference identifies the next child in the list. Class access * required by derived classes. */ NodeProxy _nextNode; /** * This node ia part of a double-linked list that belongs to its parent. * This reference identifies the previous child in the list. Class access * required by derived classes. */ NodeProxy _prevNode; /** * The parent of this node or null if the node has no parent. Class access * required by derived classes. */ NodeProxy _parent; /** * The document owner of this node, or the document itself. If the node belongs * to any document, this will point to that document. For a document this will * point at the document itself ({@link #getOwnerDocument} will return null, * though). Class access required by derived classes. */ DocumentProxy _ownerDocument; /** * The name of this node. All nodes have names, some are dynamic (e.g. the * tag name of an element), others are static (e.g. "#document"). */ private String _nodeName; /** * The value of this node. Not all nodes support values and this might be * null for some nodes. */ private String _nodeValue; /** * The children of this node are arranged in a doubly linked lists. * This reference identifies the first child in the list. */ private NodeProxy _firstChild; /** * The children of this node are arranged in a doubly linked lists. * This reference identifies the last child in the list. */ private NodeProxy _lastChild; /** * Counts how many children nodes belong to this parent. Used to speed up * some checks. */ private int _childsCount; /** * True if this node is read-only and its contents cannot be modified. */ private boolean _readOnly; /** * Holdes a list of iterators that are observing this node of its * childern. */ private NodeIteratorListener[] _iterators; /** */ public void writeExternal (ObjectOutput out) throws IOException { out.writeObject (_nextNode); out.writeObject (_prevNode); out.writeObject (_parent); out.writeObject (_ownerDocument); out.writeObject (_nodeName); out.writeObject (_nodeValue); out.writeObject (_firstChild); out.writeObject (_lastChild); out.writeInt (_childsCount); out.writeBoolean (_readOnly); if (_iterators != null) { int len = _iterators.length; out.writeInt (len); for (int i=0; i -1) { _iterators = new NodeIteratorListener[len]; for (int i=0; i