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 + -
显示快捷键?