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