0001: /*
0002: * The Apache Software License, Version 1.1
0003: *
0004: *
0005: * Copyright (c) 2001 The Apache Software Foundation. All rights
0006: * reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Apache Software Foundation (http://www.apache.org/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Xerces" and "Apache Software Foundation" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact apache@apache.org.
0031: *
0032: * 5. Products derived from this software may not be called "Apache",
0033: * nor may "Apache" appear in their name, without prior written
0034: * permission of the Apache Software Foundation.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * This software consists of voluntary contributions made by many
0051: * individuals on behalf of the Apache Software Foundation and was
0052: * originally based on software copyright (c) 1999, International
0053: * Business Machines, Inc., http://www.apache.org. For more
0054: * information on the Apache Software Foundation, please see
0055: * <http://www.apache.org/>.
0056: */
0057:
0058: package org.apache.xerces.dom;
0059:
0060: import java.util.Enumeration;
0061: import java.util.Hashtable;
0062: import java.util.Vector;
0063:
0064: import org.w3c.dom.Attr;
0065: import org.w3c.dom.DOMException;
0066: import org.w3c.dom.DOMImplementation;
0067: import org.w3c.dom.DocumentType;
0068: import org.w3c.dom.NamedNodeMap;
0069: import org.w3c.dom.Node;
0070: import org.w3c.dom.NodeList;
0071: import org.w3c.dom.Notation;
0072: import org.w3c.dom.ProcessingInstruction;
0073: import org.w3c.dom.Text;
0074:
0075: import org.w3c.dom.events.DocumentEvent;
0076: import org.w3c.dom.events.Event;
0077: import org.w3c.dom.events.EventException;
0078: import org.w3c.dom.events.EventListener;
0079: import org.w3c.dom.events.EventTarget;
0080: import org.w3c.dom.events.MutationEvent;
0081: import org.w3c.dom.ranges.DocumentRange;
0082: import org.w3c.dom.ranges.Range;
0083: import org.w3c.dom.traversal.DocumentTraversal;
0084: import org.w3c.dom.traversal.NodeIterator;
0085: import org.w3c.dom.traversal.NodeFilter;
0086: import org.w3c.dom.traversal.TreeWalker;
0087:
0088: import org.apache.xerces.dom.events.EventImpl;
0089: import org.apache.xerces.dom.events.MutationEventImpl;
0090: import org.apache.xerces.utils.XMLCharacterProperties;
0091:
0092: /**
0093: * The Document interface represents the entire HTML or XML document.
0094: * Conceptually, it is the root of the document tree, and provides the
0095: * primary access to the document's data.
0096: * <P>
0097: * Since elements, text nodes, comments, processing instructions,
0098: * etc. cannot exist outside the context of a Document, the Document
0099: * interface also contains the factory methods needed to create these
0100: * objects. The Node objects created have a ownerDocument attribute
0101: * which associates them with the Document within whose context they
0102: * were created.
0103: * <p>
0104: * The DocumentImpl class also implements the DOM Level 2 DocumentTraversal
0105: * interface. This interface is comprised of factory methods needed to
0106: * create NodeIterators and TreeWalkers. The process of creating NodeIterator
0107: * objects also adds these references to this document.
0108: * After finishing with an iterator it is important to remove the object
0109: * using the remove methods in this implementation. This allows the release of
0110: * the references from the iterator objects to the DOM Nodes.
0111: * <p>
0112: * <b>Note:</b> When any node in the document is serialized, the
0113: * entire document is serialized along with it.
0114: *
0115: * @author Arnaud Le Hors, IBM
0116: * @author Joe Kesselman, IBM
0117: * @author Andy Clark, IBM
0118: * @author Ralf Pfeiffer, IBM
0119: * @version
0120: * @since PR-DOM-Level-1-19980818.
0121: */
0122: public class DocumentImpl extends CoreDocumentImpl implements
0123: DocumentTraversal, DocumentEvent, DocumentRange {
0124:
0125: //
0126: // Constants
0127: //
0128:
0129: /** Serialization version. */
0130: static final long serialVersionUID = 515687835542616694L;
0131:
0132: //
0133: // Data
0134: //
0135:
0136: /** Iterators */
0137: // REVISIT: Should this be transient? -Ac
0138: protected Vector iterators;
0139:
0140: /** Ranges */
0141: // REVISIT: Should this be transient? -Ac
0142: protected Vector ranges;
0143:
0144: /** Table for user data attached to this document nodes. */
0145: protected Hashtable userData;
0146:
0147: /** Table for event listeners registered to this document nodes. */
0148: protected Hashtable eventListeners;
0149:
0150: /** Bypass mutation events firing. */
0151: protected boolean mutationEvents = false;
0152:
0153: //
0154: // Constructors
0155: //
0156:
0157: /**
0158: * NON-DOM: Actually creating a Document is outside the DOM's spec,
0159: * since it has to operate in terms of a particular implementation.
0160: */
0161: public DocumentImpl() {
0162: super ();
0163: }
0164:
0165: /** Constructor. */
0166: public DocumentImpl(boolean grammarAccess) {
0167: super (grammarAccess);
0168: }
0169:
0170: /**
0171: * For DOM2 support.
0172: * The createDocument factory method is in DOMImplementation.
0173: */
0174: public DocumentImpl(DocumentType doctype) {
0175: super (doctype);
0176: }
0177:
0178: /** For DOM2 support. */
0179: public DocumentImpl(DocumentType doctype, boolean grammarAccess) {
0180: super (doctype, grammarAccess);
0181: }
0182:
0183: //
0184: // Node methods
0185: //
0186:
0187: /**
0188: * Deep-clone a document, including fixing ownerDoc for the cloned
0189: * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
0190: * protection. I've chosen to implement it by calling importNode
0191: * which is DOM Level 2.
0192: *
0193: * @return org.w3c.dom.Node
0194: * @param deep boolean, iff true replicate children
0195: */
0196: public Node cloneNode(boolean deep) {
0197:
0198: DocumentImpl newdoc = new DocumentImpl();
0199: cloneNode(newdoc, deep);
0200:
0201: // experimental
0202: newdoc.mutationEvents = mutationEvents;
0203:
0204: return newdoc;
0205:
0206: } // cloneNode(boolean):Node
0207:
0208: /**
0209: * Retrieve information describing the abilities of this particular
0210: * DOM implementation. Intended to support applications that may be
0211: * using DOMs retrieved from several different sources, potentially
0212: * with different underlying representations.
0213: */
0214: public DOMImplementation getImplementation() {
0215: // Currently implemented as a singleton, since it's hardcoded
0216: // information anyway.
0217: return DOMImplementationImpl.getDOMImplementation();
0218: }
0219:
0220: /**
0221: * Store user data related to a given node
0222: * This is a place where we could use weak references! Indeed, the node
0223: * here won't be GC'ed as long as some user data is attached to it, since
0224: * the userData table will have a reference to the node.
0225: */
0226: protected void setUserData(NodeImpl n, Object data) {
0227: if (userData == null) {
0228: userData = new Hashtable();
0229: }
0230: if (data == null) {
0231: userData.remove(n);
0232: } else {
0233: userData.put(n, data);
0234: }
0235: }
0236:
0237: /**
0238: * Retreive user data related to a given node
0239: */
0240: protected Object getUserData(NodeImpl n) {
0241: if (userData == null) {
0242: return null;
0243: }
0244: return userData.get(n);
0245: }
0246:
0247: //
0248: // DocumentTraversal methods
0249: //
0250:
0251: /**
0252: * NON-DOM extension:
0253: * Create and return a NodeIterator. The NodeIterator is
0254: * added to a list of NodeIterators so that it can be
0255: * removed to free up the DOM Nodes it references.
0256: * @see #removeNodeIterator
0257: * @see #removeNodeIterators
0258: *
0259: * @param root The root of the iterator.
0260: * @param whatToShow The whatToShow mask.
0261: * @param filter The NodeFilter installed. Null means no filter.
0262: */
0263: public NodeIterator createNodeIterator(Node root, short whatToShow,
0264: NodeFilter filter) {
0265: return createNodeIterator(root, whatToShow, filter, true);
0266: }
0267:
0268: /**
0269: * Create and return a NodeIterator. The NodeIterator is
0270: * added to a list of NodeIterators so that it can be
0271: * removed to free up the DOM Nodes it references.
0272: * @see #removeNodeIterator
0273: * @see #removeNodeIterators
0274: *
0275: * @param root The root of the iterator.
0276: * @param whatToShow The whatToShow mask.
0277: * @param filter The NodeFilter installed. Null means no filter.
0278: * @param entityReferenceExpansion true to expand the contents of
0279: * EntityReference nodes
0280: * @since WD-DOM-Level-2-19990923
0281: */
0282: public NodeIterator createNodeIterator(Node root, int whatToShow,
0283: NodeFilter filter, boolean entityReferenceExpansion) {
0284: NodeIterator iterator = new NodeIteratorImpl(this , root,
0285: whatToShow, filter, entityReferenceExpansion);
0286: if (iterators == null) {
0287: iterators = new Vector();
0288: }
0289:
0290: iterators.addElement(iterator);
0291:
0292: return iterator;
0293: }
0294:
0295: /**
0296: * NON-DOM extension:
0297: * Create and return a TreeWalker.
0298: *
0299: * @param root The root of the iterator.
0300: * @param whatToShow The whatToShow mask.
0301: * @param filter The NodeFilter installed. Null means no filter.
0302: */
0303: public TreeWalker createTreeWalker(Node root, short whatToShow,
0304: NodeFilter filter) {
0305: return createTreeWalker(root, whatToShow, filter, true);
0306: }
0307:
0308: /**
0309: * Create and return a TreeWalker.
0310: *
0311: * @param root The root of the iterator.
0312: * @param whatToShow The whatToShow mask.
0313: * @param filter The NodeFilter installed. Null means no filter.
0314: * @param entityReferenceExpansion true to expand the contents of
0315: * EntityReference nodes
0316: * @since WD-DOM-Level-2-19990923
0317: */
0318: public TreeWalker createTreeWalker(Node root, int whatToShow,
0319: NodeFilter filter, boolean entityReferenceExpansion) {
0320: if (root == null) {
0321: throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
0322: "DOM007 Not supported");
0323: }
0324: return new TreeWalkerImpl(root, whatToShow, filter,
0325: entityReferenceExpansion);
0326: }
0327:
0328: //
0329: // Not DOM Level 2. Support DocumentTraversal methods.
0330: //
0331:
0332: /** This is not called by the developer client. The
0333: * developer client uses the detach() function on the
0334: * NodeIterator itself. <p>
0335: *
0336: * This function is called from the NodeIterator#detach().
0337: */
0338: void removeNodeIterator(NodeIterator nodeIterator) {
0339:
0340: if (nodeIterator == null)
0341: return;
0342: if (iterators == null)
0343: return;
0344:
0345: iterators.removeElement(nodeIterator);
0346: }
0347:
0348: //
0349: // DocumentRange methods
0350: //
0351: /**
0352: */
0353: public Range createRange() {
0354:
0355: if (ranges == null) {
0356: ranges = new Vector();
0357: }
0358:
0359: Range range = new RangeImpl(this );
0360:
0361: ranges.addElement(range);
0362:
0363: return range;
0364:
0365: }
0366:
0367: /** Not a client function. Called by Range.detach(),
0368: * so a Range can remove itself from the list of
0369: * Ranges.
0370: */
0371: void removeRange(Range range) {
0372:
0373: if (range == null)
0374: return;
0375: if (ranges == null)
0376: return;
0377:
0378: ranges.removeElement(range);
0379: }
0380:
0381: /**
0382: * A method to be called when some text was changed in a text node,
0383: * so that live objects can be notified.
0384: */
0385: void replacedText(NodeImpl node) {
0386: // notify ranges
0387: if (ranges != null) {
0388: Enumeration enum = ranges.elements();
0389: while (enum.hasMoreElements()) {
0390: ((RangeImpl)enum.nextElement()).receiveReplacedText(node);
0391: }
0392: }
0393: }
0394:
0395: /**
0396: * A method to be called when some text was deleted from a text node,
0397: * so that live objects can be notified.
0398: */
0399: void deletedText(NodeImpl node, int offset, int count) {
0400: // notify ranges
0401: if (ranges != null) {
0402: Enumeration enum = ranges.elements();
0403: while (enum.hasMoreElements()) {
0404: ((RangeImpl)enum.nextElement()).receiveDeletedText(node,
0405: offset,
0406: count);
0407: }
0408: }
0409: }
0410:
0411: /**
0412: * A method to be called when some text was inserted into a text node,
0413: * so that live objects can be notified.
0414: */
0415: void insertedText(NodeImpl node, int offset, int count) {
0416: // notify ranges
0417: if (ranges != null) {
0418: Enumeration enum = ranges.elements();
0419: while (enum.hasMoreElements()) {
0420: ((RangeImpl)enum.nextElement()).receiveInsertedText(node,
0421: offset,
0422: count);
0423: }
0424: }
0425: }
0426:
0427: /**
0428: * A method to be called when a text node has been split,
0429: * so that live objects can be notified.
0430: */
0431: void splitData(Node node, Node newNode, int offset) {
0432: // notify ranges
0433: if (ranges != null) {
0434: Enumeration enum = ranges.elements();
0435: while (enum.hasMoreElements()) {
0436: ((RangeImpl)enum.nextElement()).receiveSplitData(node,
0437: newNode,
0438: offset);
0439: }
0440: }
0441: }
0442:
0443: //
0444: // DocumentEvent methods
0445: //
0446:
0447: /**
0448: * Introduced in DOM Level 2. Optional. <p>
0449: * Create and return Event objects.
0450: *
0451: * @param type The eventType parameter specifies the type of Event
0452: * interface to be created. If the Event interface specified is supported
0453: * by the implementation this method will return a new Event of the
0454: * interface type requested. If the Event is to be dispatched via the
0455: * dispatchEvent method the appropriate event init method must be called
0456: * after creation in order to initialize the Event's values. As an
0457: * example, a user wishing to synthesize some kind of Event would call
0458: * createEvent with the parameter "Events". The initEvent method could then
0459: * be called on the newly created Event to set the specific type of Event
0460: * to be dispatched and set its context information.
0461: * @return Newly created Event
0462: * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
0463: * does not support the type of Event interface requested
0464: * @since WD-DOM-Level-2-19990923
0465: */
0466: public Event createEvent(String type) throws DOMException {
0467: if (type.equalsIgnoreCase("Events") || "Event".equals(type))
0468: return new EventImpl();
0469: if (type.equalsIgnoreCase("MutationEvents")
0470: || "MutationEvent".equals(type))
0471: return new MutationEventImpl();
0472: else
0473: throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
0474: "DOM007 Not supported");
0475: }
0476:
0477: /**
0478: * Sets whether the DOM implementation generates mutation events
0479: * upon operations.
0480: */
0481: void setMutationEvents(boolean set) {
0482: mutationEvents = set;
0483: }
0484:
0485: /**
0486: * Returns true if the DOM implementation generates mutation events.
0487: */
0488: boolean getMutationEvents() {
0489: return mutationEvents;
0490: }
0491:
0492: /**
0493: * Store event listener registered on a given node
0494: * This is another place where we could use weak references! Indeed, the
0495: * node here won't be GC'ed as long as some listener is registered on it,
0496: * since the eventsListeners table will have a reference to the node.
0497: */
0498: protected void setEventListeners(NodeImpl n, Vector listeners) {
0499: if (eventListeners == null) {
0500: eventListeners = new Hashtable();
0501: }
0502: if (listeners == null) {
0503: eventListeners.remove(n);
0504: if (eventListeners.isEmpty()) {
0505: // stop firing events when there isn't any listener
0506: mutationEvents = false;
0507: }
0508: } else {
0509: eventListeners.put(n, listeners);
0510: // turn mutation events on
0511: mutationEvents = true;
0512: }
0513: }
0514:
0515: /**
0516: * Retreive event listener registered on a given node
0517: */
0518: protected Vector getEventListeners(NodeImpl n) {
0519: if (eventListeners == null) {
0520: return null;
0521: }
0522: return (Vector) eventListeners.get(n);
0523: }
0524:
0525: //
0526: // EventTarget support (public and internal)
0527: //
0528:
0529: //
0530: // Constants
0531: //
0532:
0533: /*
0534: * NON-DOM INTERNAL: Class LEntry is just a struct used to represent
0535: * event listeners registered with this node. Copies of this object
0536: * are hung from the nodeListeners Vector.
0537: * <p>
0538: * I considered using two vectors -- one for capture,
0539: * one for bubble -- but decided that since the list of listeners
0540: * is probably short in most cases, it might not be worth spending
0541: * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
0542: */
0543: class LEntry {
0544: String type;
0545: EventListener listener;
0546: boolean useCapture;
0547:
0548: /** NON-DOM INTERNAL: Constructor for Listener list Entry
0549: * @param type Event name (NOT event group!) to listen for.
0550: * @param listener Who gets called when event is dispatched
0551: * @param useCaptue True iff listener is registered on
0552: * capturing phase rather than at-target or bubbling
0553: */
0554: LEntry(String type, EventListener listener, boolean useCapture) {
0555: this .type = type;
0556: this .listener = listener;
0557: this .useCapture = useCapture;
0558: }
0559: } // LEntry
0560:
0561: /**
0562: * Introduced in DOM Level 2. <p> Register an event listener with this
0563: * Node. A listener may be independently registered as both Capturing and
0564: * Bubbling, but may only be registered once per role; redundant
0565: * registrations are ignored.
0566: * @param node node to add listener to
0567: * @param type Event name (NOT event group!) to listen for.
0568: * @param listener Who gets called when event is dispatched
0569: * @param useCapture True iff listener is registered on
0570: * capturing phase rather than at-target or bubbling
0571: */
0572: protected void addEventListener(NodeImpl node, String type,
0573: EventListener listener, boolean useCapture) {
0574: // We can't dispatch to blank type-name, and of course we need
0575: // a listener to dispatch to
0576: if (type == null || type.equals("") || listener == null)
0577: return;
0578:
0579: // Each listener may be registered only once per type per phase.
0580: // Simplest way to code that is to zap the previous entry, if any.
0581: removeEventListener(node, type, listener, useCapture);
0582:
0583: Vector nodeListeners = getEventListeners(node);
0584: if (nodeListeners == null) {
0585: nodeListeners = new Vector();
0586: setEventListeners(node, nodeListeners);
0587: }
0588: nodeListeners
0589: .addElement(new LEntry(type, listener, useCapture));
0590:
0591: // Record active listener
0592: LCount lc = LCount.lookup(type);
0593: if (useCapture)
0594: ++lc.captures;
0595: else
0596: ++lc.bubbles;
0597:
0598: } // addEventListener(NodeImpl,String,EventListener,boolean) :void
0599:
0600: /**
0601: * Introduced in DOM Level 2. <p> Deregister an event listener previously
0602: * registered with this Node. A listener must be independently removed
0603: * from the Capturing and Bubbling roles. Redundant removals (of listeners
0604: * not currently registered for this role) are ignored.
0605: * @param node node to remove listener from
0606: * @param type Event name (NOT event group!) to listen for.
0607: * @param listener Who gets called when event is dispatched
0608: * @param useCapture True iff listener is registered on
0609: * capturing phase rather than at-target or bubbling
0610: */
0611: protected void removeEventListener(NodeImpl node, String type,
0612: EventListener listener, boolean useCapture) {
0613: // If this couldn't be a valid listener registration, ignore request
0614: if (type == null || type.equals("") || listener == null)
0615: return;
0616: Vector nodeListeners = getEventListeners(node);
0617: if (nodeListeners == null)
0618: return;
0619:
0620: // Note that addListener has previously ensured that
0621: // each listener may be registered only once per type per phase.
0622: // count-down is OK for deletions!
0623: for (int i = nodeListeners.size() - 1; i >= 0; --i) {
0624: LEntry le = (LEntry) nodeListeners.elementAt(i);
0625: if (le.useCapture == useCapture && le.listener == listener
0626: && le.type.equals(type)) {
0627: nodeListeners.removeElementAt(i);
0628: // Storage management: Discard empty listener lists
0629: if (nodeListeners.size() == 0)
0630: setEventListeners(node, null);
0631:
0632: // Remove active listener
0633: LCount lc = LCount.lookup(type);
0634: if (useCapture)
0635: --lc.captures;
0636: else
0637: --lc.bubbles;
0638:
0639: break; // Found it; no need to loop farther.
0640: }
0641: }
0642: } // removeEventListener(NodeImpl,String,EventListener,boolean) :void
0643:
0644: /**
0645: * Introduced in DOM Level 2. <p>
0646: * Distribution engine for DOM Level 2 Events.
0647: * <p>
0648: * Event propagation runs as follows:
0649: * <ol>
0650: * <li>Event is dispatched to a particular target node, which invokes
0651: * this code. Note that the event's stopPropagation flag is
0652: * cleared when dispatch begins; thereafter, if it has
0653: * been set before processing of a node commences, we instead
0654: * immediately advance to the DEFAULT phase.
0655: * <li>The node's ancestors are established as destinations for events.
0656: * For capture and bubble purposes, node ancestry is determined at
0657: * the time dispatch starts. If an event handler alters the document
0658: * tree, that does not change which nodes will be informed of the event.
0659: * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for
0660: * Capturing listeners. If found, they are invoked (see below).
0661: * <li>AT_TARGET:
0662: * Event is dispatched to NON-CAPTURING listeners on the
0663: * target node. Note that capturing listeners on this node are _not_
0664: * invoked.
0665: * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for
0666: * non-capturing listeners.
0667: * <li>Default processing: Some DOMs have default behaviors bound to
0668: * specific nodes. If this DOM does, and if the event's preventDefault
0669: * flag has not been set, we now return to the target node and process
0670: * its default handler for this event, if any.
0671: * </ol>
0672: * <p>
0673: * Note that registration of handlers during processing of an event does
0674: * not take effect during this phase of this event; they will not be called
0675: * until the next time this node is visited by dispatchEvent. On the other
0676: * hand, removals take effect immediately.
0677: * <p>
0678: * If an event handler itself causes events to be dispatched, they are
0679: * processed synchronously, before processing resumes
0680: * on the event which triggered them. Please be aware that this may
0681: * result in events arriving at listeners "out of order" relative
0682: * to the actual sequence of requests.
0683: * <p>
0684: * Note that our implementation resets the event's stop/prevent flags
0685: * when dispatch begins.
0686: * I believe the DOM's intent is that event objects be redispatchable,
0687: * though it isn't stated in those terms.
0688: * @param node node to dispatch to
0689: * @param event the event object to be dispatched to
0690: * registered EventListeners
0691: * @return true if the event's <code>preventDefault()</code>
0692: * method was invoked by an EventListener; otherwise false.
0693: */
0694: protected boolean dispatchEvent(NodeImpl node, Event event) {
0695: if (event == null)
0696: return false;
0697:
0698: // Can't use anyone else's implementation, since there's no public
0699: // API for setting the event's processing-state fields.
0700: EventImpl evt = (EventImpl) event;
0701:
0702: // VALIDATE -- must have been initialized at least once, must have
0703: // a non-null non-blank name.
0704: if (!evt.initialized || evt.type == null || evt.type.equals(""))
0705: throw new EventException(
0706: EventException.UNSPECIFIED_EVENT_TYPE_ERR,
0707: "DOM010 Unspecified event type");
0708:
0709: // If nobody is listening for this event, discard immediately
0710: LCount lc = LCount.lookup(evt.getType());
0711: if (lc.captures + lc.bubbles + lc.defaults == 0)
0712: return evt.preventDefault;
0713:
0714: // INITIALIZE THE EVENT'S DISPATCH STATUS
0715: // (Note that Event objects are reusable in our implementation;
0716: // that doesn't seem to be explicitly guaranteed in the DOM, but
0717: // I believe it is the intent.)
0718: evt.target = node;
0719: evt.stopPropagation = false;
0720: evt.preventDefault = false;
0721:
0722: // Capture pre-event parentage chain, not including target;
0723: // use pre-event-dispatch ancestors even if event handlers mutate
0724: // document and change the target's context.
0725: // Note that this is parents ONLY; events do not
0726: // cross the Attr/Element "blood/brain barrier".
0727: // DOMAttrModified. which looks like an exception,
0728: // is issued to the Element rather than the Attr
0729: // and causes a _second_ DOMSubtreeModified in the Element's
0730: // tree.
0731: Vector pv = new Vector(10, 10);
0732: Node p = node;
0733: Node n = p.getParentNode();
0734: while (n != null) {
0735: pv.addElement(n);
0736: p = n;
0737: n = n.getParentNode();
0738: }
0739:
0740: // CAPTURING_PHASE:
0741: if (lc.captures > 0) {
0742: evt.eventPhase = Event.CAPTURING_PHASE;
0743: // Ancestors are scanned, root to target, for
0744: // Capturing listeners.
0745: for (int j = pv.size() - 1; j >= 0; --j) {
0746: if (evt.stopPropagation)
0747: break; // Someone set the flag. Phase ends.
0748:
0749: // Handle all capturing listeners on this node
0750: NodeImpl nn = (NodeImpl) pv.elementAt(j);
0751: evt.currentTarget = nn;
0752: Vector nodeListeners = getEventListeners(nn);
0753: if (nodeListeners != null) {
0754: Vector nl = (Vector) nodeListeners.clone();
0755: // count-down more efficient
0756: for (int i = nl.size() - 1; i >= 0; --i) {
0757: LEntry le = (LEntry) nl.elementAt(i);
0758: if (le.useCapture && le.type.equals(evt.type)
0759: && nodeListeners.contains(le)) {
0760: try {
0761: le.listener.handleEvent(evt);
0762: } catch (Exception e) {
0763: // All exceptions are ignored.
0764: }
0765: }
0766: }
0767: }
0768: }
0769: }
0770:
0771: // Both AT_TARGET and BUBBLE use non-capturing listeners.
0772: if (lc.bubbles > 0) {
0773: // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners
0774: // on the target node. Note that capturing listeners on the target
0775: // node are _not_ invoked, even during the capture phase.
0776: evt.eventPhase = Event.AT_TARGET;
0777: evt.currentTarget = node;
0778: Vector nodeListeners = getEventListeners(node);
0779: if (!evt.stopPropagation && nodeListeners != null) {
0780: Vector nl = (Vector) nodeListeners.clone();
0781: // count-down is more efficient
0782: for (int i = nl.size() - 1; i >= 0; --i) {
0783: LEntry le = (LEntry) nl.elementAt(i);
0784: if (!le.useCapture && le.type.equals(evt.type)
0785: && nodeListeners.contains(le)) {
0786: try {
0787: le.listener.handleEvent(evt);
0788: } catch (Exception e) {
0789: // All exceptions are ignored.
0790: }
0791: }
0792: }
0793: }
0794: // BUBBLING_PHASE: Ancestors are scanned, target to root, for
0795: // non-capturing listeners. If the event's preventBubbling flag
0796: // has been set before processing of a node commences, we
0797: // instead immediately advance to the default phase.
0798: // Note that not all events bubble.
0799: if (evt.bubbles) {
0800: evt.eventPhase = Event.BUBBLING_PHASE;
0801: for (int j = 0; j < pv.size(); ++j) {
0802: if (evt.stopPropagation)
0803: break; // Someone set the flag. Phase ends.
0804:
0805: // Handle all bubbling listeners on this node
0806: NodeImpl nn = (NodeImpl) pv.elementAt(j);
0807: evt.currentTarget = nn;
0808: nodeListeners = getEventListeners(nn);
0809: if (nodeListeners != null) {
0810: Vector nl = (Vector) nodeListeners.clone();
0811: // count-down more efficient
0812: for (int i = nl.size() - 1; i >= 0; --i) {
0813: LEntry le = (LEntry) nl.elementAt(i);
0814: if (!le.useCapture
0815: && le.type.equals(evt.type)
0816: && nodeListeners.contains(le)) {
0817: try {
0818: le.listener.handleEvent(evt);
0819: } catch (Exception e) {
0820: // All exceptions are ignored.
0821: }
0822: }
0823: }
0824: }
0825: }
0826: }
0827: }
0828:
0829: // DEFAULT PHASE: Some DOMs have default behaviors bound to specific
0830: // nodes. If this DOM does, and if the event's preventDefault flag has
0831: // not been set, we now return to the target node and process its
0832: // default handler for this event, if any.
0833: // No specific phase value defined, since this is DOM-internal
0834: if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) {
0835: // evt.eventPhase = Event.DEFAULT_PHASE;
0836: // evt.currentTarget = node;
0837: // DO_DEFAULT_OPERATION
0838: }
0839:
0840: return evt.preventDefault;
0841: } // dispatchEvent(NodeImpl,Event) :boolean
0842:
0843: /**
0844: * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom...
0845: * are dispatched to an entire subtree. This is the distribution code
0846: * therefor. They DO NOT bubble, thanks be, but may be captured.
0847: * <p>
0848: * ***** At the moment I'm being sloppy and using the normal
0849: * capture dispatcher on every node. This could be optimized hugely
0850: * by writing a capture engine that tracks our position in the tree to
0851: * update the capture chain without repeated chases up to root.
0852: * @param node node to dispatch to
0853: * @param n node which was directly inserted or removed
0854: * @param e event to be sent to that node and its subtree
0855: */
0856: protected void dispatchEventToSubtree(NodeImpl node, Node n, Event e) {
0857: Vector nodeListeners = getEventListeners(node);
0858: if (nodeListeners == null || n == null)
0859: return;
0860:
0861: // ***** Recursive implementation. This is excessively expensive,
0862: // and should be replaced in conjunction with optimization
0863: // mentioned above.
0864: ((NodeImpl) n).dispatchEvent(e);
0865: if (n.getNodeType() == Node.ELEMENT_NODE) {
0866: NamedNodeMap a = n.getAttributes();
0867: for (int i = a.getLength() - 1; i >= 0; --i)
0868: dispatchEventToSubtree(node, a.item(i), e);
0869: }
0870: dispatchEventToSubtree(node, n.getFirstChild(), e);
0871: dispatchEventToSubtree(node, n.getNextSibling(), e);
0872: } // dispatchEventToSubtree(NodeImpl,Node,Event) :void
0873:
0874: /**
0875: * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
0876: * (two values, the Attr node affected (if any) and its previous
0877: * string value. Simple struct, no methods.
0878: */
0879: class EnclosingAttr {
0880: AttrImpl node;
0881: String oldvalue;
0882: }
0883:
0884: EnclosingAttr savedEnclosingAttr;
0885:
0886: /**
0887: * NON-DOM INTERNAL: Convenience wrapper for calling
0888: * dispatchAggregateEvents when the context was established
0889: * by <code>savedEnclosingAttr</code>.
0890: * @param node node to dispatch to
0891: * @param ea description of Attr affected by current operation
0892: */
0893: protected void dispatchAggregateEvents(NodeImpl node,
0894: EnclosingAttr ea) {
0895: if (ea != null)
0896: dispatchAggregateEvents(node, ea.node, ea.oldvalue,
0897: MutationEvent.MODIFICATION);
0898: else
0899: dispatchAggregateEvents(node, null, null, (short) 0);
0900:
0901: } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
0902:
0903: /**
0904: * NON-DOM INTERNAL: Generate the "aggregated" post-mutation events
0905: * DOMAttrModified and DOMSubtreeModified.
0906: * Both of these should be issued only once for each user-requested
0907: * mutation operation, even if that involves multiple changes to
0908: * the DOM.
0909: * For example, if a DOM operation makes multiple changes to a single
0910: * Attr before returning, it would be nice to generate only one
0911: * DOMAttrModified, and multiple changes over larger scope but within
0912: * a recognizable single subtree might want to generate only one
0913: * DOMSubtreeModified, sent to their lowest common ancestor.
0914: * <p>
0915: * To manage this, use the "internal" versions of insert and remove
0916: * with MUTATION_LOCAL, then make an explicit call to this routine
0917: * at the higher level. Some examples now exist in our code.
0918: *
0919: * @param node The node to dispatch to
0920: * @param enclosingAttr The Attr node (if any) whose value has been changed
0921: * as a result of the DOM operation. Null if none such.
0922: * @param oldValue The String value previously held by the
0923: * enclosingAttr. Ignored if none such.
0924: * @param change Type of modification to the attr. See
0925: * MutationEvent.attrChange
0926: */
0927: protected void dispatchAggregateEvents(NodeImpl node,
0928: AttrImpl enclosingAttr, String oldvalue, short change) {
0929: // We have to send DOMAttrModified.
0930: NodeImpl owner = null;
0931: if (enclosingAttr != null) {
0932: LCount lc = LCount
0933: .lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
0934: owner = (NodeImpl) enclosingAttr.getOwnerElement();
0935: if (lc.captures + lc.bubbles + lc.defaults > 0) {
0936: if (owner != null) {
0937: MutationEventImpl me = new MutationEventImpl();
0938: me.initMutationEvent(
0939: MutationEventImpl.DOM_ATTR_MODIFIED, true,
0940: false, enclosingAttr, oldvalue,
0941: enclosingAttr.getNodeValue(), enclosingAttr
0942: .getNodeName(), change);
0943: owner.dispatchEvent(me);
0944: }
0945: }
0946: }
0947: // DOMSubtreeModified gets sent to the lowest common root of a
0948: // set of changes.
0949: // "This event is dispatched after all other events caused by the
0950: // mutation have been fired."
0951: LCount lc = LCount
0952: .lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED);
0953: if (lc.captures + lc.bubbles + lc.defaults > 0) {
0954: MutationEvent me = new MutationEventImpl();
0955: me.initMutationEvent(
0956: MutationEventImpl.DOM_SUBTREE_MODIFIED, true,
0957: false, null, null, null, null, (short) 0);
0958:
0959: // If we're within an Attr, DStM gets sent to the Attr
0960: // and to its owningElement. Otherwise we dispatch it
0961: // locally.
0962: if (enclosingAttr != null) {
0963: dispatchEvent(enclosingAttr, me);
0964: if (owner != null)
0965: dispatchEvent(owner, me);
0966: } else
0967: dispatchEvent(node, me);
0968: }
0969: } // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void
0970:
0971: /**
0972: * NON-DOM INTERNAL: Pre-mutation context check, in
0973: * preparation for later generating DOMAttrModified events.
0974: * Determines whether this node is within an Attr
0975: * @param node node to get enclosing attribute for
0976: * @return either a description of that Attr, or null if none such.
0977: */
0978: protected void saveEnclosingAttr(NodeImpl node) {
0979: savedEnclosingAttr = null;
0980: // MUTATION PREPROCESSING AND PRE-EVENTS:
0981: // If we're within the scope of an Attr and DOMAttrModified
0982: // was requested, we need to preserve its previous value for
0983: // that event.
0984: LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
0985: if (lc.captures + lc.bubbles + lc.defaults > 0) {
0986: NodeImpl eventAncestor = node;
0987: while (true) {
0988: if (eventAncestor == null)
0989: return;
0990: int type = eventAncestor.getNodeType();
0991: if (type == Node.ATTRIBUTE_NODE) {
0992: EnclosingAttr retval = new EnclosingAttr();
0993: retval.node = (AttrImpl) eventAncestor;
0994: retval.oldvalue = retval.node.getNodeValue();
0995: savedEnclosingAttr = retval;
0996: return;
0997: } else if (type == Node.ENTITY_REFERENCE_NODE)
0998: eventAncestor = eventAncestor.parentNode();
0999: else
1000: return;
1001: // Any other parent means we're not in an Attr
1002: }
1003: }
1004: } // saveEnclosingAttr(NodeImpl) :void
1005:
1006: /**
1007: * A method to be called when a character data node has been modified
1008: */
1009: void modifyingCharacterData(NodeImpl node) {
1010: if (mutationEvents) {
1011: saveEnclosingAttr(node);
1012: }
1013: }
1014:
1015: /**
1016: * A method to be called when a character data node has been modified
1017: */
1018: void modifiedCharacterData(NodeImpl node, String oldvalue,
1019: String value) {
1020: if (mutationEvents) {
1021: // MUTATION POST-EVENTS:
1022: LCount lc = LCount
1023: .lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
1024: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1025: MutationEvent me = new MutationEventImpl();
1026: me.initMutationEvent(
1027: MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1028: true, false, null, oldvalue, value, null,
1029: (short) 0);
1030: dispatchEvent(me);
1031: }
1032:
1033: // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1034: // if required. (Common to most kinds of mutation)
1035: dispatchAggregateEvents(node, savedEnclosingAttr);
1036: } // End mutation postprocessing
1037: }
1038:
1039: /**
1040: * A method to be called when a node is about to be inserted in the tree.
1041: */
1042: void insertingNode(NodeImpl node, boolean replace) {
1043: if (mutationEvents) {
1044: if (!replace) {
1045: saveEnclosingAttr(node);
1046: }
1047: }
1048: }
1049:
1050: /**
1051: * A method to be called when a node has been inserted in the tree.
1052: */
1053: void insertedNode(NodeImpl node, NodeImpl newInternal,
1054: boolean replace) {
1055: if (mutationEvents) {
1056: // MUTATION POST-EVENTS:
1057: // "Local" events (non-aggregated)
1058: // New child is told it was inserted, and where
1059: LCount lc = LCount
1060: .lookup(MutationEventImpl.DOM_NODE_INSERTED);
1061: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1062: MutationEventImpl me = new MutationEventImpl();
1063: me.initMutationEvent(
1064: MutationEventImpl.DOM_NODE_INSERTED, true,
1065: false, node, null, null, null, (short) 0);
1066: dispatchEvent(newInternal, me);
1067: }
1068:
1069: // If within the Document, tell the subtree it's been added
1070: // to the Doc.
1071: lc = LCount
1072: .lookup(MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
1073: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1074: NodeImpl eventAncestor = node;
1075: if (savedEnclosingAttr != null)
1076: eventAncestor = (NodeImpl) savedEnclosingAttr.node
1077: .getOwnerElement();
1078: if (eventAncestor != null) { // Might have been orphan Attr
1079: NodeImpl p = eventAncestor;
1080: while (p != null) {
1081: eventAncestor = p; // Last non-null ancestor
1082: // In this context, ancestry includes
1083: // walking back from Attr to Element
1084: if (p.getNodeType() == ATTRIBUTE_NODE) {
1085: p = (NodeImpl) ((AttrImpl) p)
1086: .getOwnerElement();
1087: } else {
1088: p = p.parentNode();
1089: }
1090: }
1091: if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE) {
1092: MutationEventImpl me = new MutationEventImpl();
1093: me
1094: .initMutationEvent(
1095: MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT,
1096: false, false, null, null, null,
1097: null, (short) 0);
1098: dispatchEventToSubtree(node, newInternal, me);
1099: }
1100: }
1101: }
1102: if (!replace) {
1103: // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
1104: // (Common to most kinds of mutation)
1105: dispatchAggregateEvents(node, savedEnclosingAttr);
1106: }
1107: }
1108: }
1109:
1110: /**
1111: * A method to be called when a node is about to be removed from the tree.
1112: */
1113: void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
1114:
1115: // notify iterators
1116: if (iterators != null) {
1117: Enumeration enum = iterators.elements();
1118: while (enum.hasMoreElements()) {
1119: ((NodeIteratorImpl)enum.nextElement()).removeNode(oldChild);
1120: }
1121: }
1122:
1123: // notify ranges
1124: if (ranges != null) {
1125: Enumeration enum = ranges.elements();
1126: while (enum.hasMoreElements()) {
1127: ((RangeImpl)enum.nextElement()).removeNode(oldChild);
1128: }
1129: }
1130:
1131: // mutation events
1132: if (mutationEvents) {
1133: // MUTATION PREPROCESSING AND PRE-EVENTS:
1134: // If we're within the scope of an Attr and DOMAttrModified
1135: // was requested, we need to preserve its previous value for
1136: // that event.
1137: if (!replace) {
1138: saveEnclosingAttr(node);
1139: }
1140: // Child is told that it is about to be removed
1141: LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
1142: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1143: MutationEventImpl me= new MutationEventImpl();
1144: me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
1145: true, false, node, null,
1146: null, null, (short) 0);
1147: dispatchEvent(oldChild, me);
1148: }
1149:
1150: // If within Document, child's subtree is informed that it's
1151: // losing that status
1152: lc = LCount.lookup(
1153: MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
1154: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1155: NodeImpl eventAncestor = this ;
1156: if(savedEnclosingAttr != null)
1157: eventAncestor = (NodeImpl)
1158: savedEnclosingAttr.node.getOwnerElement();
1159: if (eventAncestor != null) { // Might have been orphan Attr
1160: for (NodeImpl p = eventAncestor.parentNode();
1161: p != null; p = p.parentNode()) {
1162: eventAncestor = p; // Last non-null ancestor
1163: }
1164: if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
1165: MutationEventImpl me = new MutationEventImpl();
1166: me.initMutationEvent(
1167: MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
1168: false, false, null,
1169: null, null, null, (short) 0);
1170: dispatchEventToSubtree(node, oldChild, me);
1171: }
1172: }
1173: }
1174: } // End mutation preprocessing
1175: }
1176:
1177: /**
1178: * A method to be called when a node has been removed from the tree.
1179: */
1180: void removedNode(NodeImpl node, boolean replace) {
1181: if (mutationEvents) {
1182: // MUTATION POST-EVENTS:
1183: // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1184: // if required. (Common to most kinds of mutation)
1185: if (!replace) {
1186: dispatchAggregateEvents(node, savedEnclosingAttr);
1187: }
1188: } // End mutation postprocessing
1189: }
1190:
1191: /**
1192: * A method to be called when a node is about to be replaced in the tree.
1193: */
1194: void replacingNode(NodeImpl node) {
1195: if (mutationEvents) {
1196: saveEnclosingAttr(node);
1197: }
1198: }
1199:
1200: /**
1201: * A method to be called when a node has been replaced in the tree.
1202: */
1203: void replacedNode(NodeImpl node) {
1204: if (mutationEvents) {
1205: dispatchAggregateEvents(node, savedEnclosingAttr);
1206: }
1207: }
1208:
1209: /**
1210: * A method to be called when an attribute value has been modified
1211: */
1212: void modifiedAttrValue(AttrImpl attr, String oldvalue) {
1213: if (mutationEvents) {
1214: // MUTATION POST-EVENTS:
1215: dispatchAggregateEvents(attr, attr, oldvalue,
1216: MutationEvent.MODIFICATION);
1217: }
1218: }
1219:
1220: /**
1221: * A method to be called when an attribute node has been set
1222: */
1223: void setAttrNode(AttrImpl attr, AttrImpl previous) {
1224: if (mutationEvents) {
1225: // MUTATION POST-EVENTS:
1226: if (previous == null) {
1227: dispatchAggregateEvents(attr.ownerNode, attr, null,
1228: MutationEvent.ADDITION);
1229: } else {
1230: dispatchAggregateEvents(attr.ownerNode, attr, previous
1231: .getNodeValue(), MutationEvent.MODIFICATION);
1232: }
1233: }
1234: }
1235:
1236: /**
1237: * A method to be called when an attribute node has been removed
1238: */
1239: void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
1240: // We can't use the standard dispatchAggregate, since it assumes
1241: // that the Attr is still attached to an owner. This code is
1242: // similar but dispatches to the previous owner, "element".
1243: if (mutationEvents) {
1244: // If we have to send DOMAttrModified (determined earlier),
1245: // do so.
1246: LCount lc = LCount
1247: .lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1248: if (lc.captures + lc.bubbles + lc.defaults > 0) {
1249: MutationEventImpl me = new MutationEventImpl();
1250: me.initMutationEvent(
1251: MutationEventImpl.DOM_ATTR_MODIFIED, true,
1252: false, null, attr.getNodeValue(), null, name,
1253: MutationEvent.REMOVAL);
1254: dispatchEvent(oldOwner, me);
1255: }
1256:
1257: // We can hand off to process DOMSubtreeModified, though.
1258: // Note that only the Element needs to be informed; the
1259: // Attr's subtree has not been changed by this operation.
1260: dispatchAggregateEvents(oldOwner, null, null, (short) 0);
1261: }
1262: }
1263:
1264: } // class DocumentImpl
|