All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.tree.ParentNode Maven / Gradle / Ivy

The newest version!
/*
 * $Id: ParentNode.java,v 1.9 1999/05/17 23:50:27 mode Exp $
 * 
 * Copyright (c) 1998-1999 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 */

package com.sun.xml.tree;


import java.io.IOException;
import java.io.Writer;

import org.xml.sax.SAXException;

import org.w3c.dom.*;


/**
 * This adds an implementation of "parent of" relationships to the NodeBase
 * class.  It implements operations for maintaining a set of children,
 * providing indexed access to them, and writing them them out as text.
 *
 * 

The NodeList it implements to describe its children is "live", as * required by DOM. That means that indexed accesses by applications must * handle cases associated with unstable indices and lengths. Indices * should not be stored if they can be invalidated by changes, including * changes made by other threads. * * @author David Brownell * @version $Revision: 1.9 $ */ // not public ... javadoc looks a bit odd (hidden base class) // but it's only subclassable within this package anyway abstract class ParentNode extends NodeBase implements XmlReadable { private NodeBase children []; private int length; /** * Builds a ParentNode, which can have children that are * subclasses of NodeBase. */ // package private ParentNode () { } /** * Called to minimize space utilization. Affects only * this node; children must be individually trimmed. */ public void trimToSize () { if (length == 0) children = null; else if (children.length != length) { NodeBase temp [] = new NodeBase [length]; System.arraycopy (children, 0, temp, 0, length); children = temp; } } // package private void reduceWaste () { if (children == null) return; // // Arbitrary -- rather than paying trimToSize() costs // on most elements, we routinely accept some waste but // do try to reduce egregious waste. Interacts with // the array allocation done in appendChild. // if ((children.length - length) > 6) trimToSize (); } /** * Writes each of the child nodes. For element nodes, this adds * whitespace to indent non-text children (it prettyprints) unless * the xml:space='preserve' attribute applies, or the * write context disables prettyprinting. * * @param context describes how the children should be printed */ public void writeChildrenXml (XmlWriteContext context) throws IOException { if (children == null) return; int oldIndent = 0; boolean preserve = true; boolean pureText = true; if (getNodeType () == ELEMENT_NODE) { preserve = "preserve".equals ( getInheritedAttribute ("xml:space")); oldIndent = context.getIndentLevel (); } try { if (!preserve) context.setIndentLevel (oldIndent + 2); for (int i = 0; i < length; i++) { if (!preserve && children [i].getNodeType () != TEXT_NODE) { context.printIndent (); pureText = false; } children [i].writeXml (context); } } finally { if (!preserve) { context.setIndentLevel (oldIndent); if (!pureText) context.printIndent (); // for ETag } } } /** * Subclasses may override this method, which is called shortly after * the object type is known and before any children are processed. * For elements, attributes are known and may be modified; since * parent context is available, inherited attributes can be seen. * *

The default implementation does nothing. * * @param context provides location and error reporting data */ public void startParse (ParseContext context) throws SAXException { // nothing } /** * Subclasses may override this method, which is called after each * child (text, element, processing instruction) is fully parsed. * Subclassers may substitute, discard, reorder, modify, or otherwise * process the child. For example, elements which correspond to object * properties may be stored in that way, rather than appended. The * startParse method has always been called before this. * *

The default implementation does nothing. * * @param child the child which has just been completely parsed; * it is already a child of this node. * @param context provides location and error reporting data */ public void doneChild (NodeEx child, ParseContext context) throws SAXException { } /** * Subclasses may override this method, which is called shortly after * the object is fully parsed. The startParse method has * always been called before this, and doneChild will have been called * for each child. * *

The default implementation does nothing. * * @param context provides location and error reporting data */ public void doneParse (ParseContext context) throws SAXException { // nothing } // package private -- overridden in implementation classes abstract void checkChildType (int type) throws DOMException; // DOM support /** * DOM: Returns true if there are children to this node. */ final public boolean hasChildNodes () { return length > 0; } /** * DOM: Returns the first child of this node, else null if there * are no children. */ final public Node getFirstChild () { if (length == 0) return null; return children [0]; } /** * DOM: Returns the last child of this node, else null if there * are no children. */ final public Node getLastChild () { if (length == 0) return null; return children [length - 1]; } /** DOM: Returns the number of children */ final public int getLength () { return length; } /** DOM: Returns the Nth child, or null */ final public Node item (int i) { if (length == 0 || i >= length) return null; try { return children [i]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } // groups all the "wrong document/implementation" checks private NodeBase checkDocument (Node newChild) throws DOMException { if (newChild == null) throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR); // check for wrong implementation if (!(newChild instanceof NodeBase)) throw new DomEx (DomEx.WRONG_DOCUMENT_ERR); Document owner = newChild.getOwnerDocument (); XmlDocument myOwner = ownerDocument; NodeBase child = (NodeBase) newChild; // bizarre DOM special case for document if (myOwner == null && this instanceof XmlDocument) myOwner = (XmlDocument) this; // check for wrong document if (owner != null && owner != myOwner) throw new DomEx (DomEx.WRONG_DOCUMENT_ERR); // permit "unowned" NodeBase children to be added, // e.g. if someone constructs an ElementNode directly if (owner == null) { child.setOwnerDocument (myOwner); } if (child.hasChildNodes ()) { for (int i = 0; true; i++) { Node node = child.item (i); if (node == null) break; if (node.getOwnerDocument () == null) ((NodeBase)node).setOwnerDocument (myOwner); else if (node.getOwnerDocument () != myOwner) throw new DomEx (DomEx.WRONG_DOCUMENT_ERR); } } return child; } // makes sure that child isn't an ancestor of this private void checkNotAncestor (Node newChild) throws DOMException { // text, etc ... if (!newChild.hasChildNodes ()) return; Node ancestor = this; while (ancestor != null) { if (newChild == ancestor) throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR); ancestor = ancestor.getParentNode (); } } // update mutation count private void mutated () { XmlDocument doc = ownerDocument; if (doc == null && this instanceof XmlDocument) doc = (XmlDocument) this; if (doc != null) doc.mutationCount++; } // // When fragments are appended/inserted/replaced, their entire // contents get moved and the fragment becomes empty. // private void consumeFragment (Node fragment, Node before) throws DOMException { ParentNode frag = (ParentNode) fragment; Node temp; // don't start insertions we can't complete for (int i = 0; (temp = frag.item (i)) != null; i++) { checkNotAncestor (temp); checkChildType (temp.getNodeType ()); } while ((temp = frag.item (0)) != null) insertBefore (temp, before); } /** * DOM: Appends the child to the set of this node's children. * The new child must belong to this document. * * @param newChild the new child to be appended */ public Node appendChild (Node newChild) throws DOMException { NodeBase child; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); child = checkDocument (newChild); if (newChild.getNodeType () == DOCUMENT_FRAGMENT_NODE) { consumeFragment (newChild, null); return newChild; } checkNotAncestor (newChild); checkChildType (child.getNodeType ()); // this is the only place this vector needs allocating, // though it may also need to be grown in insertBefore. // most elements have very few children if (children == null) children = new NodeBase [3]; else if (children.length == length) { NodeBase temp [] = new NodeBase [length * 2]; System.arraycopy (children, 0, temp, 0, length); children = temp; } child.setParentNode (this, length); children [length++] = child; mutated (); return child; } /** * DOM: Inserts the new child before the specified child, * which if null indicates appending the new child to the * current set of children. The new child must belong to * this particular document. * * @param newChild the new child to be inserted * @param refChild node before which newChild is to be inserted */ public Node insertBefore (Node newChild, Node refChild) throws DOMException { NodeBase child; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (refChild == null) return appendChild (newChild); if (length == 0) throw new DomEx (DomEx.NOT_FOUND_ERR); child = checkDocument (newChild); if (newChild.getNodeType () == DOCUMENT_FRAGMENT_NODE) { consumeFragment (newChild, refChild); return newChild; } checkNotAncestor (newChild); checkChildType (newChild.getNodeType ()); // grow array if needed if (children.length == length) { NodeBase temp [] = new NodeBase [length * 2]; System.arraycopy (children, 0, temp, 0, length); children = temp; } for (int i = 0; i < length; i++) { if (children [i] != refChild) continue; child.setParentNode (this, i); System.arraycopy (children, i, children, i + 1, length - i); children [i] = child; length++; mutated (); return newChild; } throw new DomEx (DomEx.NOT_FOUND_ERR); } /** * DOM: Replaces the specified child with the new node, * returning the original child or throwing an exception. * The new child must belong to this particular document. * * @param newChild the new child to be inserted * @param refChild node which is to be replaced */ public Node replaceChild (Node newChild, Node refChild) throws DOMException { NodeBase child; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (newChild == null || refChild == null) throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR); if (children == null) throw new DomEx (DomEx.NOT_FOUND_ERR); child = checkDocument (newChild); if (newChild.getNodeType () == DOCUMENT_FRAGMENT_NODE) { consumeFragment (newChild, refChild); return removeChild (refChild); } checkNotAncestor (newChild); checkChildType (newChild.getNodeType ()); for (int i = 0; i < length; i++) { if (children [i] != refChild) continue; child.setParentNode (this, i); children [i] = child; ((NodeBase) refChild).setParentNode (null, -1); mutated (); return refChild; } throw new DomEx (DomEx.NOT_FOUND_ERR); } /** * DOM: removes child if present, returning argument. * * @param oldChild the node which is to be removed */ public Node removeChild (Node oldChild) throws DOMException { NodeBase child; if (readonly) throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR); if (!(oldChild instanceof NodeBase)) throw new DomEx (DomEx.NOT_FOUND_ERR); child = (NodeBase) oldChild; for (int i = 0; i < length; i++) { if (children [i] != child) continue; if ((i + 1) != length) System.arraycopy (children, i + 1, children, i, (length - 1) - i); length--; children [length] = null; child.setParentNode (null, -1); mutated (); return oldChild; } throw new DomEx (DomEx.NOT_FOUND_ERR); } /** * DOM: Returns a "live" list view of the elements below this * one which have the specified tag name. Because this is "live", this * API is dangerous -- indices are not stable in the face of most tree * updates. Use a TreeWalker instead. * * @param tagname the tag name to show; or "*" for all elements. * @return list of such elements */ public NodeList getElementsByTagName (String tagname) { if ("*".equals (tagname)) tagname = null; return new TagList (tagname); } // // Slightly optimized to track document mutation count. For now // we assume that a 32 bit counter won't wrap around, and that // there's no point in caching list length. // class TagList implements NodeList { private String tag; private int lastMutationCount; private int lastIndex; private TreeWalker lastWalker; private int getLastMutationCount () { XmlDocument doc = (XmlDocument) getOwnerDocument (); return (doc == null) ? 0 : doc.mutationCount; } TagList (String tag) { this.tag = tag; } public Node item (int i) { if (i < 0) return null; int temp = getLastMutationCount (); // Can we try to reuse the last walker? if (lastWalker != null) { if (i < lastIndex || temp != lastMutationCount) lastWalker = null; } // if not, get a new one ... if (lastWalker == null) { lastWalker = new TreeWalker (ParentNode.this); lastIndex = -1; lastMutationCount = temp; } if (i == lastIndex) return lastWalker.getCurrent (); Node node = null; while (i > lastIndex && (node = lastWalker.getNextElement (tag)) != null) lastIndex++; return node; } public int getLength () { TreeWalker walker = new TreeWalker (ParentNode.this); Node node = null; int retval; for (retval = 0; (node = walker.getNextElement (tag)) != null; retval++) continue; return retval; } } /** * Returns the index of the node in the list of children, such * that item() will return that child. * * @param maybeChild the node which may be a child of this one * @return the index of the node in the set of children, or * else -1 if that node is not a child */ final public int getIndexOf (Node maybeChild) { for (int i = 0; i < length; i++) if (children [i] == maybeChild) return i; return -1; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy