domnode.java

来自「kaffe Java 解释器语言,源码,Java的子集系统,开放源代码」· Java 代码 · 共 1,568 行 · 第 1/3 页

JAVA
1,568
字号
/* * Copyright (C) 1999-2001 David Brownell *  * This file is part of GNU JAXP, a library. * * GNU JAXP is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *  * GNU JAXP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * As a special exception, if you link this library with other files to * produce an executable, this library does not by itself cause the * resulting executable to be covered by the GNU General Public License. * This exception does not however invalidate any other reasons why the * executable file might be covered by the GNU General Public License.  */package gnu.xml.dom;import org.w3c.dom.*;import org.w3c.dom.events.*;import org.w3c.dom.traversal.*;/** * <p> "Node", "EventTarget", and "DocumentEvent" implementation. * This provides most of the core DOM functionality; only more * specialized features are provided by subclasses.  Those subclasses may * have some particular constraints they must implement, by overriding * methods defined here.  Such constraints are noted here in the method * documentation. </p> * * <p> Note that you can create events with type names prefixed with "USER-", * and pass them through this DOM.  This lets you use the DOM event scheme * for application specific purposes, although you must use a predefined event * structure (such as MutationEvent) to pass data along with those events. * Test for existence of this feature with the "USER-Events" DOM feature * name.</p> * * <p> Other kinds of events you can send include the "html" events, * like "load", "unload", "abort", "error", and "blur"; and the mutation * events.  If this DOM has been compiled with mutation event support * enabled, it will send mutation events when you change parts of the * tree; otherwise you may create and send such events yourself, but * they won't be generated by the DOM itself. </p> * * <p> Note that there is a namespace-aware name comparison method, * <em>nameAndTypeEquals</em>, which compares the names (and types) of * two nodes in conformance with the "Namespaces in XML" specification. * While mostly intended for use with elements and attributes, this should * also be helpful for ProcessingInstruction nodes and some others which * do not have namespace URIs. * * @author David Brownell */public abstract class DomNode    implements Node, NodeList, EventTarget, DocumentEvent, Cloneable{    //    // CLASS DATA    //    // tunable    //	NKIDS_* affects arrays of children (which grow)    // (currently) fixed size:    //	ANCESTORS_* is for event capture/bubbling, # ancestors    //	NOTIFICATIONS_* is for per-node event delivery, # events    private static final int		NKIDS_INIT = 5;    private static final int		NKIDS_DELTA = 8;    private static final int		ANCESTORS_INIT = 20;    private static final int		NOTIFICATIONS_INIT = 10;    // tunable: enable mutation events or not?  Enabling it costs about    // 10-15% in DOM construction time, last time it was measured.    // package private !!!    static final boolean		reportMutations = true;    // locking protocol changeable only within this class    private static final Object		lockNode = new Object ();    // optimize space to share what we can    private static final DomNode	noKids [] = new DomNode [0];    // NON-FINAL class data    // Optimize event dispatch by not allocating memory each time    private static boolean		dispatchDataLock;    private static DomNode		ancestors []		    = new DomNode [ANCESTORS_INIT];    private static ListenerRecord	notificationSet []		    = new ListenerRecord [NOTIFICATIONS_INIT];    // Ditto for the (most common) event object itself!    private static boolean		eventDataLock;    private static DomEvent.DomMutationEvent	mutationEvent		    = new DomEvent.DomMutationEvent (null);        //    // PER-INSTANCE DATA    //    private Document			owner;    private DomNode			parent;    // Bleech ... "package private" so a builder can populate entity refs.    // writable during construction.  DOM spec is nasty.    boolean				readonly;    // children    private DomNode			children [];    private int				length;    // event registrations    private ListenerRecord		listeners [];    private int				nListeners;    // Optimize access to siblings by caching indices.    private transient int		parentIndex;	//	// Some of the methods here are declared 'final' because	// knowledge about their implementation is built into this	// class -- for both integrity and performance.	//    // package private    void nyi ()    {	throw new DomEx (DomEx.NOT_SUPPORTED_ERR,		"feature not yet implemented", this, 0);    }    /**     * Reduces space utilization for this node.     */    public void compact ()    {	if (children != null && children != noKids) {	    if (length == 0)		children = noKids;	    // allow a bit of fuzz (max NKIDS_DELTA).	    // the JVM won't always use less memory for smaller arrays...	    else if ((children.length - length) > 1) {		DomNode	newKids [] = new DomNode [length];		System.arraycopy (children, 0, newKids, 0, length);		children = newKids;	    }	}	if (listeners != null && listeners.length != nListeners) {	    if (nListeners == 0)		listeners = null;	    else {		ListenerRecord	l [] = new ListenerRecord [nListeners];		System.arraycopy (listeners, 0, l, 0, nListeners);		listeners = l;	    }	}    }    /**     * Constructs a node and associates it with its owner.  Only     * Document and DocumentType nodes may be created with no owner,     * and DocumentType nodes get an owner as soon as they are     * associated with a document.     */    protected DomNode (Document owner)    {	short 	type = getNodeType ();	if (owner == null) {	    // DOM calls never go down this path	    if (type != DOCUMENT_NODE && type != DOCUMENT_TYPE_NODE)		throw new IllegalArgumentException ("no owner!");	}	this.owner = owner;	switch (type) {	    case DOCUMENT_NODE:	    case DOCUMENT_FRAGMENT_NODE:	    case ENTITY_REFERENCE_NODE:	    case ELEMENT_NODE:		children = new DomNode [NKIDS_INIT];		break;	    // no sane app wants the attributes-with-children model	    case ATTRIBUTE_NODE:		children = new DomNode [1];		break;	    // we don't currently build children with entities	    case ENTITY_NODE:		children = noKids;	    // no other kinds of nodes may have children; so for	    // such nodes, length stays zero, children stays null	}    }    /**     * <b>DOM L1</b>     * Returns null; Element subclasses must override this method.     */    public NamedNodeMap getAttributes ()	{ return null; }        /**     * <b>DOM L2></b>     * Returns true iff this is an element node with attributes.     */    public boolean hasAttributes ()	{ return false; }    /**     * <b>DOM L1</b>     * Returns a list, possibly empty, of the children of this node.     * In this implementation, to conserve memory, nodes are the same     * as their list of children.  This can have ramifications for     * subclasses, which may need to provide their own getLength method     * for reasons unrelated to the NodeList method of the same name.     */    public NodeList getChildNodes ()	{ return this; }    /**     * <b>DOM L1</b>     * Returns the first child of this node, or null if there are none.     */    final public Node getFirstChild ()	{ return item (0); }    /**     * <b>DOM L1</b>     * Returns the last child of this node, or null if there are none.     */    final public Node getLastChild ()	{ return item (length - 1); }    /**     * <b>DOM L1</b>     * Returns true if this node has children.     */    final public boolean hasChildNodes ()	{ return length > 0; }    /**     * Exposes the internal "readonly" flag.  In DOM, children of     * entities and entity references are readonly, as are the     * objects associated with DocumentType objets.     */    final public boolean isReadonly ()    {	return readonly;    }        /**     * Sets the internal "readonly" flag so this subtree can't be changed.     * Subclasses need to override this method for any associated content     * that's not a child node, such as an element's attributes or the     * (few) declarations associated with a DocumentType.     */    public void makeReadonly ()    {	readonly = true;	for (int i = 0; i < length; i++)	    children [i].makeReadonly ();    }    // we need to have at least N more kids    private void ensureEnough (int n)    {	if ((children.length - length) > n)	    return;		// don't grow in micro-chunks	if (n < NKIDS_DELTA)	    n = NKIDS_DELTA;	n += children.length;	DomNode newKids [] = new DomNode [n];	for (int i = 0; i < length; i++)	    newKids [i] = children [i];	children = newKids;    }    // just checks the node for inclusion -- may be called many    // times (docfrag) before anything is allowed to change    private void checkMisc (DomNode child)    {	if (readonly)	    throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR,		    null, this, 0);	if (children == null)	    throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR,		    null, this, 0);	if (parent != null && child.length > 0) {	    for (Node temp = parent;		    temp != null;		    temp = temp.getParentNode ()) {		if (child == parent)		    throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR,			    "can't make ancestor into a child", this, 0);	    }	}		Node myOwner = owner;	Node newOwner = child.owner;	short nodeType = getNodeType ();	short newType = child.getNodeType ();	if (nodeType == DOCUMENT_NODE)	    myOwner = this;	if (newOwner != myOwner) {	    // new in DOM L2, this case -- patch it up later, in reparent()	    if (!(newType == DOCUMENT_TYPE_NODE && newOwner == null))		throw new DomEx (DomEx.WRONG_DOCUMENT_ERR,			null, child, 0);	}	// Test code.	// 	System.out.println("DOMNode node type = " + nodeType	// 			   + " new type: " + newType);		// 	if (newType == 3)	// 	  System.out.println("child content = " + child.getNodeValue());		// enforce various structural constraints	switch (nodeType) {	    case DOCUMENT_NODE:		if (newType == ELEMENT_NODE			|| newType == PROCESSING_INSTRUCTION_NODE			|| newType == COMMENT_NODE			|| newType == DOCUMENT_TYPE_NODE)		    return;		break;	    case ATTRIBUTE_NODE:		if (newType == TEXT_NODE || newType == ENTITY_REFERENCE_NODE)		    return;		break;	    case DOCUMENT_FRAGMENT_NODE:	    case ENTITY_REFERENCE_NODE:	    case ELEMENT_NODE:	    case ENTITY_NODE:		if (newType == ELEMENT_NODE			|| newType == TEXT_NODE			|| newType == COMMENT_NODE			|| newType == PROCESSING_INSTRUCTION_NODE			|| newType == CDATA_SECTION_NODE			|| newType == ENTITY_REFERENCE_NODE)		    return;	}	throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR,	    "this node can't have that type of child", this, 0);    }    //    // NOTE:  after this method, the new child knows its parent,    // but the parent doesn't know the child.  Don't let that    // intermediate state be seen by the application.    //    // XXX prefer to pass in a mutation event object, making removeChild reuse    // it appropriately.  That'll shorten critical paths, and remove the    // guarantee that the three-message replaceChild case will hit the heap.    //    private void reparent (DomNode newChild)    {	short childType = newChild.getNodeType ();	if (getNodeType () == DOCUMENT_NODE		&& childType == DOCUMENT_TYPE_NODE) {	    DomDoctype	doctype = (DomDoctype) newChild;	    if (doctype.getImplementation ()		    != ((Document)this).getImplementation ())		throw new DomEx (DomEx.WRONG_DOCUMENT_ERR,			"implementation mismatch", newChild, 0);	    newChild.owner = (Document) this;	}	// get rid of old parent	Node		oldParent = newChild.parent;	if (oldParent != null)	    oldParent.removeChild (newChild);		if (childType != ATTRIBUTE_NODE)	    newChild.parent  = this;    }    // Here's hoping a good optimizer will detect the case when the    // next several methods are never called, and won't allocate    // object code space of any kind.  (Case:  not reporting any    // mutation events.  We can also remove some static variables    // listed above.)    private void insertionEvent (	DomEvent.DomMutationEvent	event,	DomNode				target    ) {	boolean doFree = false;	if (event == null) {	    event = getMutationEvent ();	    if (event != null)		doFree = true;	    else		event = new DomEvent.DomMutationEvent (null);	}	event.initMutationEvent ("DOMNodeInserted",		true /* bubbles */, false /* nocancel */,		this /* related */, null, null, null, (short) 0);	target.dispatchEvent (event);	// XXX should really visit every descendant of 'target'	// and sent a DOMNodeInsertedIntoDocument event to it...	// bleech, there's no way to keep that acceptably fast.	if (doFree) {	    event.target = null;	    event.relatedNode = null;	    event.currentNode = null;	    eventDataLock = false;	} // else we created work for the GC    }    private void removalEvent (	DomEvent.DomMutationEvent	event,	DomNode				target    ) {	boolean doFree = false;	if (event == null) {	    event = getMutationEvent ();	    if (event != null)		doFree = true;	    else		event = new DomEvent.DomMutationEvent (null);	}	event.initMutationEvent ("DOMNodeRemoved",		true /* bubbles */, false /* nocancel */,		this /* related */, null, null, null, (short) 0);	target.dispatchEvent (event);	// XXX should really visit every descendant of 'target'	// and sent a DOMNodeRemovedFromDocument event to it...	// bleech, there's no way to keep that acceptably fast.	event.target = null;	event.relatedNode = null;	event.currentNode = null;	if (doFree)	    eventDataLock = false;	// else we created more work for the GC    }    //    // Avoid creating lots of memory management work, by using a simple    // allocation strategy for the mutation event objects that get used    // at least once per tree modification.  We can't use stack allocation,    // so we do the next simplest thing -- more or less, static allocation.    // Concurrent notifications should be rare, anyway.    //    // Returns the preallocated object, which needs to be carefully freed,    // or null to indicate the caller needs to allocate their own.    //    static private DomEvent.DomMutationEvent getMutationEvent ()    {	synchronized (lockNode) {	    if (eventDataLock)		return null;	    eventDataLock = true;	    return mutationEvent;	}    }    // NOTE:  this is manually inlined in the insertion    // and removal event methods above; change in sync.    static private void freeMutationEvent ()    {	// clear fields to enable GC	mutationEvent.clear ();	eventDataLock = false;    }    /**     * <b>DOM L1</b>     * Appends the specified node to this node's list of children.     * Document subclasses must override this to enforce the restrictions     * that there be only one element and document type child.     *     * <p> Causes a DOMNodeInserted mutation event to be reported.     * Will first cause a DOMNodeRemoved event to be reported if the     * parameter already has a parent.  If the new child is a document     * fragment node, both events will be reported for each child of     * the fragment; the order in which children are removed and     * inserted is implementation-specific.     *     * <p> If this DOM has been compiled without mutation event support,     * these events will not be reported.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?