KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > dom > DocumentImpl


1 /*
2  * Copyright 2001,2002,2004,2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.dom;
18
19 import java.io.Serializable JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import java.util.Vector JavaDoc;
22
23 import org.apache.xerces.dom.events.EventImpl;
24 import org.apache.xerces.dom.events.MutationEventImpl;
25 import org.w3c.dom.UserDataHandler JavaDoc;
26 import org.w3c.dom.Attr JavaDoc;
27 import org.w3c.dom.DOMException JavaDoc;
28 import org.w3c.dom.DOMImplementation JavaDoc;
29 import org.w3c.dom.DocumentType JavaDoc;
30 import org.w3c.dom.Element JavaDoc;
31 import org.w3c.dom.NamedNodeMap JavaDoc;
32 import org.w3c.dom.Node JavaDoc;
33 import org.w3c.dom.events.DocumentEvent JavaDoc;
34 import org.w3c.dom.events.Event JavaDoc;
35 import org.w3c.dom.events.EventException JavaDoc;
36 import org.w3c.dom.events.EventListener JavaDoc;
37 import org.w3c.dom.events.MutationEvent JavaDoc;
38 import org.w3c.dom.ranges.DocumentRange;
39 import org.w3c.dom.ranges.Range;
40 import org.w3c.dom.traversal.DocumentTraversal;
41 import org.w3c.dom.traversal.NodeFilter;
42 import org.w3c.dom.traversal.NodeIterator;
43 import org.w3c.dom.traversal.TreeWalker;
44
45
46 /**
47  * The Document interface represents the entire HTML or XML document.
48  * Conceptually, it is the root of the document tree, and provides the
49  * primary access to the document's data.
50  * <P>
51  * Since elements, text nodes, comments, processing instructions,
52  * etc. cannot exist outside the context of a Document, the Document
53  * interface also contains the factory methods needed to create these
54  * objects. The Node objects created have a ownerDocument attribute
55  * which associates them with the Document within whose context they
56  * were created.
57  * <p>
58  * The DocumentImpl class also implements the DOM Level 2 DocumentTraversal
59  * interface. This interface is comprised of factory methods needed to
60  * create NodeIterators and TreeWalkers. The process of creating NodeIterator
61  * objects also adds these references to this document.
62  * After finishing with an iterator it is important to remove the object
63  * using the remove methods in this implementation. This allows the release of
64  * the references from the iterator objects to the DOM Nodes.
65  * <p>
66  * <b>Note:</b> When any node in the document is serialized, the
67  * entire document is serialized along with it.
68  *
69  * @xerces.internal
70  *
71  * @author Arnaud Le Hors, IBM
72  * @author Joe Kesselman, IBM
73  * @author Andy Clark, IBM
74  * @author Ralf Pfeiffer, IBM
75  * @version $Id: DocumentImpl.java,v 1.85 2005/05/02 22:02:22 mrglavas Exp $
76  * @since PR-DOM-Level-1-19980818.
77  */

78 public class DocumentImpl
79     extends CoreDocumentImpl
80     implements DocumentTraversal, DocumentEvent JavaDoc, DocumentRange {
81
82     //
83
// Constants
84
//
85

86     /** Serialization version. */
87     static final long serialVersionUID = 515687835542616694L;
88
89     //
90
// Data
91
//
92

93     /** Iterators */
94     // REVISIT: Should this be transient? -Ac
95
protected Vector JavaDoc iterators;
96
97      /** Ranges */
98     // REVISIT: Should this be transient? -Ac
99
protected Vector JavaDoc ranges;
100
101     /** Table for event listeners registered to this document nodes. */
102     protected Hashtable JavaDoc eventListeners;
103
104     /** Bypass mutation events firing. */
105     protected boolean mutationEvents = false;
106
107     //
108
// Constructors
109
//
110

111     /**
112      * NON-DOM: Actually creating a Document is outside the DOM's spec,
113      * since it has to operate in terms of a particular implementation.
114      */

115     public DocumentImpl() {
116         super();
117     }
118
119     /** Constructor. */
120     public DocumentImpl(boolean grammarAccess) {
121         super(grammarAccess);
122     }
123
124     /**
125      * For DOM2 support.
126      * The createDocument factory method is in DOMImplementation.
127      */

128     public DocumentImpl(DocumentType JavaDoc doctype)
129     {
130         super(doctype);
131     }
132
133     /** For DOM2 support. */
134     public DocumentImpl(DocumentType JavaDoc doctype, boolean grammarAccess) {
135         super(doctype, grammarAccess);
136     }
137
138     //
139
// Node methods
140
//
141

142     /**
143      * Deep-clone a document, including fixing ownerDoc for the cloned
144      * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
145      * protection. I've chosen to implement it by calling importNode
146      * which is DOM Level 2.
147      *
148      * @return org.w3c.dom.Node
149      * @param deep boolean, iff true replicate children
150      */

151     public Node JavaDoc cloneNode(boolean deep) {
152
153         DocumentImpl newdoc = new DocumentImpl();
154         callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
155         cloneNode(newdoc, deep);
156
157         // experimental
158
newdoc.mutationEvents = mutationEvents;
159
160         return newdoc;
161
162     } // cloneNode(boolean):Node
163

164     /**
165      * Retrieve information describing the abilities of this particular
166      * DOM implementation. Intended to support applications that may be
167      * using DOMs retrieved from several different sources, potentially
168      * with different underlying representations.
169      */

170     public DOMImplementation JavaDoc getImplementation() {
171         // Currently implemented as a singleton, since it's hardcoded
172
// information anyway.
173
return DOMImplementationImpl.getDOMImplementation();
174     }
175
176     //
177
// DocumentTraversal methods
178
//
179

180     /**
181      * NON-DOM extension:
182      * Create and return a NodeIterator. The NodeIterator is
183      * added to a list of NodeIterators so that it can be
184      * removed to free up the DOM Nodes it references.
185      *
186      * @param root The root of the iterator.
187      * @param whatToShow The whatToShow mask.
188      * @param filter The NodeFilter installed. Null means no filter.
189      */

190     public NodeIterator createNodeIterator(Node JavaDoc root,
191                                            short whatToShow,
192                                            NodeFilter filter)
193     {
194         return createNodeIterator(root, whatToShow, filter, true);
195     }
196
197     /**
198      * Create and return a NodeIterator. The NodeIterator is
199      * added to a list of NodeIterators so that it can be
200      * removed to free up the DOM Nodes it references.
201      *
202      * @param root The root of the iterator.
203      * @param whatToShow The whatToShow mask.
204      * @param filter The NodeFilter installed. Null means no filter.
205      * @param entityReferenceExpansion true to expand the contents of
206      * EntityReference nodes
207      * @since WD-DOM-Level-2-19990923
208      */

209     public NodeIterator createNodeIterator(Node JavaDoc root,
210                                            int whatToShow,
211                                            NodeFilter filter,
212                                            boolean entityReferenceExpansion)
213     {
214         
215         if (root == null) {
216                   String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
217                   throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
218         }
219
220         NodeIterator iterator = new NodeIteratorImpl(this,
221                                                      root,
222                                                      whatToShow,
223                                                      filter,
224                                                      entityReferenceExpansion);
225         if (iterators == null) {
226             iterators = new Vector JavaDoc();
227         }
228
229         iterators.addElement(iterator);
230
231         return iterator;
232     }
233
234     /**
235      * NON-DOM extension:
236      * Create and return a TreeWalker.
237      *
238      * @param root The root of the iterator.
239      * @param whatToShow The whatToShow mask.
240      * @param filter The NodeFilter installed. Null means no filter.
241      */

242     public TreeWalker createTreeWalker(Node JavaDoc root,
243                                        short whatToShow,
244                                        NodeFilter filter)
245     {
246         return createTreeWalker(root, whatToShow, filter, true);
247     }
248     /**
249      * Create and return a TreeWalker.
250      *
251      * @param root The root of the iterator.
252      * @param whatToShow The whatToShow mask.
253      * @param filter The NodeFilter installed. Null means no filter.
254      * @param entityReferenceExpansion true to expand the contents of
255      * EntityReference nodes
256      * @since WD-DOM-Level-2-19990923
257      */

258     public TreeWalker createTreeWalker(Node JavaDoc root,
259                                        int whatToShow,
260                                        NodeFilter filter,
261                                        boolean entityReferenceExpansion)
262     {
263         if (root == null) {
264             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
265             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
266         }
267         return new TreeWalkerImpl(root, whatToShow, filter,
268                                   entityReferenceExpansion);
269     }
270
271     //
272
// Not DOM Level 2. Support DocumentTraversal methods.
273
//
274

275     /** This is not called by the developer client. The
276      * developer client uses the detach() function on the
277      * NodeIterator itself. <p>
278      *
279      * This function is called from the NodeIterator#detach().
280      */

281      void removeNodeIterator(NodeIterator nodeIterator) {
282
283         if (nodeIterator == null) return;
284         if (iterators == null) return;
285
286         iterators.removeElement(nodeIterator);
287     }
288
289     //
290
// DocumentRange methods
291
//
292
/**
293      */

294     public Range createRange() {
295
296         if (ranges == null) {
297             ranges = new Vector JavaDoc();
298         }
299
300         Range range = new RangeImpl(this);
301
302         ranges.addElement(range);
303
304         return range;
305
306     }
307
308     /** Not a client function. Called by Range.detach(),
309      * so a Range can remove itself from the list of
310      * Ranges.
311      */

312     void removeRange(Range range) {
313
314         if (range == null) return;
315         if (ranges == null) return;
316
317         ranges.removeElement(range);
318     }
319
320     /**
321      * A method to be called when some text was changed in a text node,
322      * so that live objects can be notified.
323      */

324     void replacedText(NodeImpl node) {
325         // notify ranges
326
if (ranges != null) {
327             int size = ranges.size();
328             for (int i = 0; i != size; i++) {
329                 ((RangeImpl)ranges.elementAt(i)).receiveReplacedText(node);
330             }
331         }
332     }
333
334     /**
335      * A method to be called when some text was deleted from a text node,
336      * so that live objects can be notified.
337      */

338     void deletedText(NodeImpl node, int offset, int count) {
339         // notify ranges
340
if (ranges != null) {
341             int size = ranges.size();
342             for (int i = 0; i != size; i++) {
343                 ((RangeImpl)ranges.elementAt(i)).receiveDeletedText(node,
344                                                                 offset, count);
345             }
346         }
347     }
348
349     /**
350      * A method to be called when some text was inserted into a text node,
351      * so that live objects can be notified.
352      */

353     void insertedText(NodeImpl node, int offset, int count) {
354         // notify ranges
355
if (ranges != null) {
356             int size = ranges.size();
357             for (int i = 0; i != size; i++) {
358                 ((RangeImpl)ranges.elementAt(i)).receiveInsertedText(node,
359                                                                 offset, count);
360             }
361         }
362     }
363
364     /**
365      * A method to be called when a text node has been split,
366      * so that live objects can be notified.
367      */

368     void splitData(Node JavaDoc node, Node JavaDoc newNode, int offset) {
369         // notify ranges
370
if (ranges != null) {
371             int size = ranges.size();
372             for (int i = 0; i != size; i++) {
373                 ((RangeImpl)ranges.elementAt(i)).receiveSplitData(node,
374                                                               newNode, offset);
375             }
376         }
377     }
378
379     //
380
// DocumentEvent methods
381
//
382

383     /**
384      * Introduced in DOM Level 2. Optional. <p>
385      * Create and return Event objects.
386      *
387      * @param type The eventType parameter specifies the type of Event
388      * interface to be created. If the Event interface specified is supported
389      * by the implementation this method will return a new Event of the
390      * interface type requested. If the Event is to be dispatched via the
391      * dispatchEvent method the appropriate event init method must be called
392      * after creation in order to initialize the Event's values. As an
393      * example, a user wishing to synthesize some kind of Event would call
394      * createEvent with the parameter "Events". The initEvent method could then
395      * be called on the newly created Event to set the specific type of Event
396      * to be dispatched and set its context information.
397      * @return Newly created Event
398      * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
399      * does not support the type of Event interface requested
400      * @since WD-DOM-Level-2-19990923
401      */

402     public Event JavaDoc createEvent(String JavaDoc type)
403     throws DOMException JavaDoc {
404         if (type.equalsIgnoreCase("Events") || "Event".equals(type))
405             return new EventImpl();
406         if (type.equalsIgnoreCase("MutationEvents") ||
407                 "MutationEvent".equals(type))
408             return new MutationEventImpl();
409         else {
410             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
411             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
412         }
413     }
414
415     /**
416      * Sets whether the DOM implementation generates mutation events
417      * upon operations.
418      */

419     void setMutationEvents(boolean set) {
420         mutationEvents = set;
421     }
422
423     /**
424      * Returns true if the DOM implementation generates mutation events.
425      */

426     boolean getMutationEvents() {
427         return mutationEvents;
428     }
429
430     /**
431      * Store event listener registered on a given node
432      * This is another place where we could use weak references! Indeed, the
433      * node here won't be GC'ed as long as some listener is registered on it,
434      * since the eventsListeners table will have a reference to the node.
435      */

436     protected void setEventListeners(NodeImpl n, Vector JavaDoc listeners) {
437         if (eventListeners == null) {
438             eventListeners = new Hashtable JavaDoc();
439         }
440         if (listeners == null) {
441             eventListeners.remove(n);
442             if (eventListeners.isEmpty()) {
443                 // stop firing events when there isn't any listener
444
mutationEvents = false;
445             }
446         } else {
447             eventListeners.put(n, listeners);
448             // turn mutation events on
449
mutationEvents = true;
450         }
451     }
452
453     /**
454      * Retreive event listener registered on a given node
455      */

456     protected Vector JavaDoc getEventListeners(NodeImpl n) {
457         if (eventListeners == null) {
458             return null;
459         }
460         return (Vector JavaDoc) eventListeners.get(n);
461     }
462
463     //
464
// EventTarget support (public and internal)
465
//
466

467     //
468
// Constants
469
//
470

471     /*
472      * NON-DOM INTERNAL: Class LEntry is just a struct used to represent
473      * event listeners registered with this node. Copies of this object
474      * are hung from the nodeListeners Vector.
475      * <p>
476      * I considered using two vectors -- one for capture,
477      * one for bubble -- but decided that since the list of listeners
478      * is probably short in most cases, it might not be worth spending
479      * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
480      */

481     class LEntry implements Serializable JavaDoc {
482
483         private static final long serialVersionUID = 3258416144514626360L;
484         String JavaDoc type;
485         EventListener JavaDoc listener;
486         boolean useCapture;
487         
488         /** NON-DOM INTERNAL: Constructor for Listener list Entry
489          * @param type Event name (NOT event group!) to listen for.
490          * @param listener Who gets called when event is dispatched
491          * @param useCaptue True iff listener is registered on
492          * capturing phase rather than at-target or bubbling
493          */

494         LEntry(String JavaDoc type, EventListener JavaDoc listener, boolean useCapture)
495         {
496             this.type = type;
497             this.listener = listener;
498             this.useCapture = useCapture;
499         }
500
501     } // LEntry
502

503     /**
504      * Introduced in DOM Level 2. <p> Register an event listener with this
505      * Node. A listener may be independently registered as both Capturing and
506      * Bubbling, but may only be registered once per role; redundant
507      * registrations are ignored.
508      * @param node node to add listener to
509      * @param type Event name (NOT event group!) to listen for.
510      * @param listener Who gets called when event is dispatched
511      * @param useCapture True iff listener is registered on
512      * capturing phase rather than at-target or bubbling
513      */

514     protected void addEventListener(NodeImpl node, String JavaDoc type,
515                                     EventListener JavaDoc listener, boolean useCapture)
516     {
517         // We can't dispatch to blank type-name, and of course we need
518
// a listener to dispatch to
519
if (type == null || type.equals("") || listener == null)
520             return;
521       
522         // Each listener may be registered only once per type per phase.
523
// Simplest way to code that is to zap the previous entry, if any.
524
removeEventListener(node, type, listener, useCapture);
525         
526         Vector JavaDoc nodeListeners = getEventListeners(node);
527         if(nodeListeners == null) {
528             nodeListeners = new Vector JavaDoc();
529             setEventListeners(node, nodeListeners);
530         }
531         nodeListeners.addElement(new LEntry(type, listener, useCapture));
532         
533         // Record active listener
534
LCount lc = LCount.lookup(type);
535         if (useCapture) {
536             ++lc.captures;
537             ++lc.total;
538         }
539         else {
540             ++lc.bubbles;
541             ++lc.total;
542         }
543
544     } // addEventListener(NodeImpl,String,EventListener,boolean) :void
545

546     /**
547      * Introduced in DOM Level 2. <p> Deregister an event listener previously
548      * registered with this Node. A listener must be independently removed
549      * from the Capturing and Bubbling roles. Redundant removals (of listeners
550      * not currently registered for this role) are ignored.
551      * @param node node to remove listener from
552      * @param type Event name (NOT event group!) to listen for.
553      * @param listener Who gets called when event is dispatched
554      * @param useCapture True iff listener is registered on
555      * capturing phase rather than at-target or bubbling
556      */

557     protected void removeEventListener(NodeImpl node, String JavaDoc type,
558                                        EventListener JavaDoc listener,
559                                        boolean useCapture)
560     {
561         // If this couldn't be a valid listener registration, ignore request
562
if (type == null || type.equals("") || listener == null)
563             return;
564         Vector JavaDoc nodeListeners = getEventListeners(node);
565         if (nodeListeners == null)
566             return;
567
568         // Note that addListener has previously ensured that
569
// each listener may be registered only once per type per phase.
570
// count-down is OK for deletions!
571
for (int i = nodeListeners.size() - 1; i >= 0; --i) {
572             LEntry le = (LEntry) nodeListeners.elementAt(i);
573             if (le.useCapture == useCapture && le.listener == listener &&
574                 le.type.equals(type)) {
575                 nodeListeners.removeElementAt(i);
576                 // Storage management: Discard empty listener lists
577
if (nodeListeners.size() == 0)
578                     setEventListeners(node, null);
579
580                 // Remove active listener
581
LCount lc = LCount.lookup(type);
582                 if (useCapture) {
583                     --lc.captures;
584                     --lc.total;
585                 }
586                 else {
587                     --lc.bubbles;
588                     --lc.total;
589                 }
590
591                 break; // Found it; no need to loop farther.
592
}
593         }
594     } // removeEventListener(NodeImpl,String,EventListener,boolean) :void
595

596     protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
597         Vector JavaDoc nodeListeners = getEventListeners(src);
598     if (nodeListeners == null) {
599         return;
600     }
601     setEventListeners(tgt, (Vector JavaDoc) nodeListeners.clone());
602     }
603
604     /**
605      * Introduced in DOM Level 2. <p>
606      * Distribution engine for DOM Level 2 Events.
607      * <p>
608      * Event propagation runs as follows:
609      * <ol>
610      * <li>Event is dispatched to a particular target node, which invokes
611      * this code. Note that the event's stopPropagation flag is
612      * cleared when dispatch begins; thereafter, if it has
613      * been set before processing of a node commences, we instead
614      * immediately advance to the DEFAULT phase.
615      * <li>The node's ancestors are established as destinations for events.
616      * For capture and bubble purposes, node ancestry is determined at
617      * the time dispatch starts. If an event handler alters the document
618      * tree, that does not change which nodes will be informed of the event.
619      * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for
620      * Capturing listeners. If found, they are invoked (see below).
621      * <li>AT_TARGET:
622      * Event is dispatched to NON-CAPTURING listeners on the
623      * target node. Note that capturing listeners on this node are _not_
624      * invoked.
625      * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for
626      * non-capturing listeners.
627      * <li>Default processing: Some DOMs have default behaviors bound to
628      * specific nodes. If this DOM does, and if the event's preventDefault
629      * flag has not been set, we now return to the target node and process
630      * its default handler for this event, if any.
631      * </ol>
632      * <p>
633      * Note that registration of handlers during processing of an event does
634      * not take effect during this phase of this event; they will not be called
635      * until the next time this node is visited by dispatchEvent. On the other
636      * hand, removals take effect immediately.
637      * <p>
638      * If an event handler itself causes events to be dispatched, they are
639      * processed synchronously, before processing resumes
640      * on the event which triggered them. Please be aware that this may
641      * result in events arriving at listeners "out of order" relative
642      * to the actual sequence of requests.
643      * <p>
644      * Note that our implementation resets the event's stop/prevent flags
645      * when dispatch begins.
646      * I believe the DOM's intent is that event objects be redispatchable,
647      * though it isn't stated in those terms.
648      * @param node node to dispatch to
649      * @param event the event object to be dispatched to
650      * registered EventListeners
651      * @return true if the event's <code>preventDefault()</code>
652      * method was invoked by an EventListener; otherwise false.
653     */

654     protected boolean dispatchEvent(NodeImpl node, Event JavaDoc event) {
655         if (event == null) return false;
656         
657         // Can't use anyone else's implementation, since there's no public
658
// API for setting the event's processing-state fields.
659
EventImpl evt = (EventImpl)event;
660
661         // VALIDATE -- must have been initialized at least once, must have
662
// a non-null non-blank name.
663
if(!evt.initialized || evt.type == null || evt.type.equals("")) {
664             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "UNSPECIFIED_EVENT_TYPE_ERR", null);
665             throw new EventException JavaDoc(EventException.UNSPECIFIED_EVENT_TYPE_ERR, msg);
666         }
667         
668         // If nobody is listening for this event, discard immediately
669
LCount lc = LCount.lookup(evt.getType());
670         if (lc.total == 0)
671             return evt.preventDefault;
672
673         // INITIALIZE THE EVENT'S DISPATCH STATUS
674
// (Note that Event objects are reusable in our implementation;
675
// that doesn't seem to be explicitly guaranteed in the DOM, but
676
// I believe it is the intent.)
677
evt.target = node;
678         evt.stopPropagation = false;
679         evt.preventDefault = false;
680         
681         // Capture pre-event parentage chain, not including target;
682
// use pre-event-dispatch ancestors even if event handlers mutate
683
// document and change the target's context.
684
// Note that this is parents ONLY; events do not
685
// cross the Attr/Element "blood/brain barrier".
686
// DOMAttrModified. which looks like an exception,
687
// is issued to the Element rather than the Attr
688
// and causes a _second_ DOMSubtreeModified in the Element's
689
// tree.
690
Vector JavaDoc pv = new Vector JavaDoc(10,10);
691         Node JavaDoc p = node;
692         Node JavaDoc n = p.getParentNode();
693         while (n != null) {
694             pv.addElement(n);
695             p = n;
696             n = n.getParentNode();
697         }
698         
699         // CAPTURING_PHASE:
700
if (lc.captures > 0) {
701             evt.eventPhase = Event.CAPTURING_PHASE;
702             // Ancestors are scanned, root to target, for
703
// Capturing listeners.
704
for (int j = pv.size() - 1; j >= 0; --j) {
705                 if (evt.stopPropagation)
706                     break; // Someone set the flag. Phase ends.
707

708                 // Handle all capturing listeners on this node
709
NodeImpl nn = (NodeImpl) pv.elementAt(j);
710                 evt.currentTarget = nn;
711                 Vector JavaDoc nodeListeners = getEventListeners(nn);
712                 if (nodeListeners != null) {
713                     Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
714                     // call listeners in the order in which they got registered
715
int nlsize = nl.size();
716                     for (int i = 0; i < nlsize; i++) {
717                         LEntry le = (LEntry) nl.elementAt(i);
718                         if (le.useCapture && le.type.equals(evt.type) &&
719                             nodeListeners.contains(le)) {
720                             try {
721                                 le.listener.handleEvent(evt);
722                             }
723                             catch (Exception JavaDoc e) {
724                                 // All exceptions are ignored.
725
}
726                         }
727                     }
728                 }
729             }
730         }
731         
732         
733         // Both AT_TARGET and BUBBLE use non-capturing listeners.
734
if (lc.bubbles > 0) {
735             // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners
736
// on the target node. Note that capturing listeners on the target
737
// node are _not_ invoked, even during the capture phase.
738
evt.eventPhase = Event.AT_TARGET;
739             evt.currentTarget = node;
740             Vector JavaDoc nodeListeners = getEventListeners(node);
741             if (!evt.stopPropagation && nodeListeners != null) {
742                 Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
743                 // call listeners in the order in which they got registered
744
int nlsize = nl.size();
745                 for (int i = 0; i < nlsize; i++) {
746                     LEntry le = (LEntry) nl.elementAt(i);
747                     if (!le.useCapture && le.type.equals(evt.type) &&
748                         nodeListeners.contains(le)) {
749                         try {
750                             le.listener.handleEvent(evt);
751                         }
752                         catch (Exception JavaDoc e) {
753                             // All exceptions are ignored.
754
}
755                     }
756                 }
757             }
758             // BUBBLING_PHASE: Ancestors are scanned, target to root, for
759
// non-capturing listeners. If the event's preventBubbling flag
760
// has been set before processing of a node commences, we
761
// instead immediately advance to the default phase.
762
// Note that not all events bubble.
763
if (evt.bubbles) {
764                 evt.eventPhase = Event.BUBBLING_PHASE;
765                 int pvsize = pv.size();
766                 for (int j = 0; j < pvsize; j++) {
767                     if (evt.stopPropagation)
768                         break; // Someone set the flag. Phase ends.
769

770                     // Handle all bubbling listeners on this node
771
NodeImpl nn = (NodeImpl) pv.elementAt(j);
772                     evt.currentTarget = nn;
773                     nodeListeners = getEventListeners(nn);
774                     if (nodeListeners != null) {
775                         Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
776                         // call listeners in the order in which they got
777
// registered
778
int nlsize = nl.size();
779                         for (int i = 0; i < nlsize; i++) {
780                             LEntry le = (LEntry) nl.elementAt(i);
781                             if (!le.useCapture && le.type.equals(evt.type) &&
782                                 nodeListeners.contains(le)) {
783                                 try {
784                                     le.listener.handleEvent(evt);
785                                 }
786                                 catch (Exception JavaDoc e) {
787                                     // All exceptions are ignored.
788
}
789                             }
790                         }
791                     }
792                 }
793             }
794         }
795         
796         // DEFAULT PHASE: Some DOMs have default behaviors bound to specific
797
// nodes. If this DOM does, and if the event's preventDefault flag has
798
// not been set, we now return to the target node and process its
799
// default handler for this event, if any.
800
// No specific phase value defined, since this is DOM-internal
801
if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) {
802             // evt.eventPhase = Event.DEFAULT_PHASE;
803
// evt.currentTarget = node;
804
// DO_DEFAULT_OPERATION
805
}
806
807         return evt.preventDefault;
808     } // dispatchEvent(NodeImpl,Event) :boolean
809

810     /**
811      * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom...
812      * are dispatched to an entire subtree. This is the distribution code
813      * therefor. They DO NOT bubble, thanks be, but may be captured.
814      * <p>
815      * Similar to code in dispatchingEventToSubtree however this method
816      * is only used on the target node and does not start a dispatching chain
817      * on the sibling of the target node as this is not part of the subtree
818      * ***** At the moment I'm being sloppy and using the normal
819      * capture dispatcher on every node. This could be optimized hugely
820      * by writing a capture engine that tracks our position in the tree to
821      * update the capture chain without repeated chases up to root.
822      * @param n target node (that was directly inserted or removed)
823      * @param e event to be sent to that node and its subtree
824      */

825     protected void dispatchEventToSubtree(Node JavaDoc n, Event JavaDoc e) {
826         
827         ((NodeImpl) n).dispatchEvent(e);
828         if (n.getNodeType() == Node.ELEMENT_NODE) {
829             NamedNodeMap JavaDoc a = n.getAttributes();
830             for (int i = a.getLength() - 1; i >= 0; --i)
831                 dispatchingEventToSubtree(a.item(i), e);
832         }
833         dispatchingEventToSubtree(n.getFirstChild(), e);
834         
835     } // dispatchEventToSubtree(NodeImpl,Node,Event) :void
836

837
838     /**
839      * Dispatches event to the target node's descendents recursively
840      *
841      * @param n node to dispatch to
842      * @param e event to be sent to that node and its subtree
843      */

844     protected void dispatchingEventToSubtree(Node JavaDoc n, Event JavaDoc e) {
845         if (n==null)
846             return;
847         
848         // ***** Recursive implementation. This is excessively expensive,
849
// and should be replaced in conjunction with optimization
850
// mentioned above.
851
((NodeImpl) n).dispatchEvent(e);
852         if (n.getNodeType() == Node.ELEMENT_NODE) {
853             NamedNodeMap JavaDoc a = n.getAttributes();
854             for (int i = a.getLength() - 1; i >= 0; --i)
855                 dispatchingEventToSubtree(a.item(i), e);
856         }
857         dispatchingEventToSubtree(n.getFirstChild(), e);
858         dispatchingEventToSubtree(n.getNextSibling(), e);
859     }
860     
861     /**
862      * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
863      * (two values, the Attr node affected (if any) and its previous
864      * string value. Simple struct, no methods.
865      */

866     class EnclosingAttr implements Serializable JavaDoc {
867         private static final long serialVersionUID = 3257001077260759859L;
868         AttrImpl node;
869         String JavaDoc oldvalue;
870     }
871
872     EnclosingAttr savedEnclosingAttr;
873
874     /**
875      * NON-DOM INTERNAL: Convenience wrapper for calling
876      * dispatchAggregateEvents when the context was established
877      * by <code>savedEnclosingAttr</code>.
878      * @param node node to dispatch to
879      * @param ea description of Attr affected by current operation
880      */

881     protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) {
882         if (ea != null)
883             dispatchAggregateEvents(node, ea.node, ea.oldvalue,
884                                     MutationEvent.MODIFICATION);
885         else
886             dispatchAggregateEvents(node, null, null, (short) 0);
887             
888     } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
889

890     /**
891      * NON-DOM INTERNAL: Generate the "aggregated" post-mutation events
892      * DOMAttrModified and DOMSubtreeModified.
893      * Both of these should be issued only once for each user-requested
894      * mutation operation, even if that involves multiple changes to
895      * the DOM.
896      * For example, if a DOM operation makes multiple changes to a single
897      * Attr before returning, it would be nice to generate only one
898      * DOMAttrModified, and multiple changes over larger scope but within
899      * a recognizable single subtree might want to generate only one
900      * DOMSubtreeModified, sent to their lowest common ancestor.
901      * <p>
902      * To manage this, use the "internal" versions of insert and remove
903      * with MUTATION_LOCAL, then make an explicit call to this routine
904      * at the higher level. Some examples now exist in our code.
905      *
906      * @param node The node to dispatch to
907      * @param enclosingAttr The Attr node (if any) whose value has been changed
908      * as a result of the DOM operation. Null if none such.
909      * @param oldValue The String value previously held by the
910      * enclosingAttr. Ignored if none such.
911      * @param change Type of modification to the attr. See
912      * MutationEvent.attrChange
913      */

914     protected void dispatchAggregateEvents(NodeImpl node,
915                                            AttrImpl enclosingAttr,
916                                            String JavaDoc oldvalue, short change) {
917         // We have to send DOMAttrModified.
918
NodeImpl owner = null;
919         if (enclosingAttr != null) {
920             LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
921             owner = (NodeImpl) enclosingAttr.getOwnerElement();
922             if (lc.total > 0) {
923                 if (owner != null) {
924                     MutationEventImpl me = new MutationEventImpl();
925                     me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
926                                          true, false, enclosingAttr,
927                                          oldvalue,
928                                          enclosingAttr.getNodeValue(),
929                                          enclosingAttr.getNodeName(),
930                                          change);
931                     owner.dispatchEvent(me);
932                 }
933             }
934         }
935         // DOMSubtreeModified gets sent to the lowest common root of a
936
// set of changes.
937
// "This event is dispatched after all other events caused by the
938
// mutation have been fired."
939
LCount lc = LCount.lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED);
940         if (lc.total > 0) {
941             MutationEvent JavaDoc me = new MutationEventImpl();
942             me.initMutationEvent(MutationEventImpl.DOM_SUBTREE_MODIFIED,
943                                  true, false, null, null,
944                                  null, null, (short) 0);
945
946             // If we're within an Attr, DStM gets sent to the Attr
947
// and to its owningElement. Otherwise we dispatch it
948
// locally.
949
if (enclosingAttr != null) {
950                 dispatchEvent(enclosingAttr, me);
951                 if (owner != null)
952                     dispatchEvent(owner, me);
953             }
954             else
955                 dispatchEvent(node, me);
956         }
957     } // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void
958

959     /**
960      * NON-DOM INTERNAL: Pre-mutation context check, in
961      * preparation for later generating DOMAttrModified events.
962      * Determines whether this node is within an Attr
963      * @param node node to get enclosing attribute for
964      * @return either a description of that Attr, or null if none such.
965      */

966     protected void saveEnclosingAttr(NodeImpl node) {
967         savedEnclosingAttr = null;
968         // MUTATION PREPROCESSING AND PRE-EVENTS:
969
// If we're within the scope of an Attr and DOMAttrModified
970
// was requested, we need to preserve its previous value for
971
// that event.
972
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
973         if (lc.total > 0) {
974             NodeImpl eventAncestor = node;
975             while (true) {
976                 if (eventAncestor == null)
977                     return;
978                 int type = eventAncestor.getNodeType();
979                 if (type == Node.ATTRIBUTE_NODE) {
980                     EnclosingAttr retval = new EnclosingAttr();
981                     retval.node = (AttrImpl) eventAncestor;
982                     retval.oldvalue = retval.node.getNodeValue();
983                     savedEnclosingAttr = retval;
984                     return;
985                 }
986                 else if (type == Node.ENTITY_REFERENCE_NODE)
987                     eventAncestor = eventAncestor.parentNode();
988                 else if (type == Node.TEXT_NODE)
989                     eventAncestor = eventAncestor.parentNode();
990                 else
991                     return;
992                 // Any other parent means we're not in an Attr
993
}
994         }
995     } // saveEnclosingAttr(NodeImpl) :void
996

997     /**
998      * A method to be called when a character data node has been modified
999      */

1000    void modifyingCharacterData(NodeImpl node, boolean replace) {
1001        if (mutationEvents) {
1002            if (!replace) {
1003                saveEnclosingAttr(node);
1004            }
1005        }
1006    }
1007
1008    /**
1009     * A method to be called when a character data node has been modified
1010     */

1011    void modifiedCharacterData(NodeImpl node, String JavaDoc oldvalue, String JavaDoc value, boolean replace) {
1012        if (mutationEvents) {
1013            if (!replace) {
1014                // MUTATION POST-EVENTS:
1015
LCount lc =
1016                    LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
1017                if (lc.total > 0) {
1018                    MutationEvent JavaDoc me = new MutationEventImpl();
1019                    me.initMutationEvent(
1020                                    MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1021                                        true, false, null,
1022                                        oldvalue, value, null, (short) 0);
1023                    dispatchEvent(node, me);
1024                }
1025            
1026                // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1027
// if required. (Common to most kinds of mutation)
1028
dispatchAggregateEvents(node, savedEnclosingAttr);
1029            } // End mutation postprocessing
1030
}
1031    }
1032    
1033    /**
1034     * A method to be called when a character data node has been replaced
1035     */

1036    void replacedCharacterData(NodeImpl node, String JavaDoc oldvalue, String JavaDoc value) {
1037        //now that we have finished replacing data, we need to perform the same actions
1038
//that are required after a character data node has been modified
1039
//send the value of false for replace parameter so that mutation
1040
//events if appropriate will be initiated
1041
modifiedCharacterData(node, oldvalue, value, false);
1042    }
1043    
1044    
1045
1046    /**
1047     * A method to be called when a node is about to be inserted in the tree.
1048     */

1049    void insertingNode(NodeImpl node, boolean replace) {
1050        if (mutationEvents) {
1051            if (!replace) {
1052                saveEnclosingAttr(node);
1053            }
1054        }
1055    }
1056
1057    /**
1058     * A method to be called when a node has been inserted in the tree.
1059     */

1060    void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
1061        if (mutationEvents) {
1062            // MUTATION POST-EVENTS:
1063
// "Local" events (non-aggregated)
1064
// New child is told it was inserted, and where
1065
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
1066            if (lc.total > 0) {
1067                MutationEventImpl me = new MutationEventImpl();
1068                me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED,
1069                                     true, false, node,
1070                                     null, null, null, (short) 0);
1071                dispatchEvent(newInternal, me);
1072            }
1073
1074            // If within the Document, tell the subtree it's been added
1075
// to the Doc.
1076
lc = LCount.lookup(
1077                            MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
1078            if (lc.total > 0) {
1079                NodeImpl eventAncestor = node;
1080                if (savedEnclosingAttr != null)
1081                    eventAncestor = (NodeImpl)
1082                        savedEnclosingAttr.node.getOwnerElement();
1083                if (eventAncestor != null) { // Might have been orphan Attr
1084
NodeImpl p = eventAncestor;
1085                    while (p != null) {
1086                        eventAncestor = p; // Last non-null ancestor
1087
// In this context, ancestry includes
1088
// walking back from Attr to Element
1089
if (p.getNodeType() == ATTRIBUTE_NODE) {
1090                            p = (NodeImpl) ((AttrImpl)p).getOwnerElement();
1091                        }
1092                        else {
1093                            p = p.parentNode();
1094                        }
1095                    }
1096                    if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
1097                        MutationEventImpl me = new MutationEventImpl();
1098                        me.initMutationEvent(MutationEventImpl
1099                                             .DOM_NODE_INSERTED_INTO_DOCUMENT,
1100                                             false,false,null,null,
1101                                             null,null,(short)0);
1102                        dispatchEventToSubtree(newInternal, me);
1103                    }
1104                }
1105            }
1106            if (!replace) {
1107                // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
1108
// (Common to most kinds of mutation)
1109
dispatchAggregateEvents(node, savedEnclosingAttr);
1110            }
1111        }
1112        
1113        // notify the range of insertions
1114
if (ranges != null) {
1115            int size = ranges.size();
1116            for (int i = 0; i != size; i++) {
1117                ((RangeImpl)ranges.elementAt(i)).insertedNodeFromDOM(newInternal);
1118            }
1119        }
1120    }
1121
1122    /**
1123     * A method to be called when a node is about to be removed from the tree.
1124     */

1125    void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
1126
1127        // notify iterators
1128
if (iterators != null) {
1129            int size = iterators.size();
1130            for (int i = 0; i != size; i++) {
1131               ((NodeIteratorImpl)iterators.elementAt(i)).removeNode(oldChild);
1132            }
1133        }
1134
1135        // notify ranges
1136
if (ranges != null) {
1137            int size = ranges.size();
1138            for (int i = 0; i != size; i++) {
1139                ((RangeImpl)ranges.elementAt(i)).removeNode(oldChild);
1140            }
1141        }
1142
1143        // mutation events
1144
if (mutationEvents) {
1145            // MUTATION PREPROCESSING AND PRE-EVENTS:
1146
// If we're within the scope of an Attr and DOMAttrModified
1147
// was requested, we need to preserve its previous value for
1148
// that event.
1149
if (!replace) {
1150                saveEnclosingAttr(node);
1151            }
1152            // Child is told that it is about to be removed
1153
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
1154            if (lc.total > 0) {
1155                MutationEventImpl me= new MutationEventImpl();
1156                me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
1157                                     true, false, node, null,
1158                                     null, null, (short) 0);
1159                dispatchEvent(oldChild, me);
1160            }
1161
1162            // If within Document, child's subtree is informed that it's
1163
// losing that status
1164
lc = LCount.lookup(
1165                             MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
1166            if (lc.total > 0) {
1167                NodeImpl eventAncestor = this;
1168                if(savedEnclosingAttr != null)
1169                    eventAncestor = (NodeImpl)
1170                        savedEnclosingAttr.node.getOwnerElement();
1171                if (eventAncestor != null) { // Might have been orphan Attr
1172
for (NodeImpl p = eventAncestor.parentNode();
1173                         p != null; p = p.parentNode()) {
1174                        eventAncestor = p; // Last non-null ancestor
1175
}
1176                    if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
1177                        MutationEventImpl me = new MutationEventImpl();
1178                        me.initMutationEvent(
1179                              MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
1180                                             false, false, null,
1181                                             null, null, null, (short) 0);
1182                        dispatchEventToSubtree(oldChild, me);
1183                    }
1184                }
1185            }
1186        } // End mutation preprocessing
1187
}
1188
1189    /**
1190     * A method to be called when a node has been removed from the tree.
1191     */

1192    void removedNode(NodeImpl node, boolean replace) {
1193        if (mutationEvents) {
1194            // MUTATION POST-EVENTS:
1195
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1196
// if required. (Common to most kinds of mutation)
1197
if (!replace) {
1198                dispatchAggregateEvents(node, savedEnclosingAttr);
1199            }
1200        } // End mutation postprocessing
1201
}
1202
1203    /**
1204     * A method to be called when a node is about to be replaced in the tree.
1205     */

1206    void replacingNode(NodeImpl node) {
1207        if (mutationEvents) {
1208            saveEnclosingAttr(node);
1209        }
1210    }
1211    
1212    /**
1213     * A method to be called when character data is about to be replaced in the tree.
1214     */

1215    void replacingData (NodeImpl node) {
1216        if (mutationEvents) {
1217                saveEnclosingAttr(node);
1218        }
1219    }
1220
1221    /**
1222     * A method to be called when a node has been replaced in the tree.
1223     */

1224    void replacedNode(NodeImpl node) {
1225        if (mutationEvents) {
1226            dispatchAggregateEvents(node, savedEnclosingAttr);
1227        }
1228    }
1229
1230    /**
1231     * A method to be called when an attribute value has been modified
1232     */

1233    void modifiedAttrValue(AttrImpl attr, String JavaDoc oldvalue) {
1234        if (mutationEvents) {
1235            // MUTATION POST-EVENTS:
1236
dispatchAggregateEvents(attr, attr, oldvalue,
1237                                    MutationEvent.MODIFICATION);
1238        }
1239    }
1240
1241    /**
1242     * A method to be called when an attribute node has been set
1243     */

1244    void setAttrNode(AttrImpl attr, AttrImpl previous) {
1245        if (mutationEvents) {
1246            // MUTATION POST-EVENTS:
1247
if (previous == null) {
1248                dispatchAggregateEvents(attr.ownerNode, attr, null,
1249                                        MutationEvent.ADDITION);
1250            }
1251            else {
1252                dispatchAggregateEvents(attr.ownerNode, attr,
1253                                        previous.getNodeValue(),
1254                                        MutationEvent.MODIFICATION);
1255            }
1256        }
1257    }
1258
1259    /**
1260     * A method to be called when an attribute node has been removed
1261     */

1262    void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String JavaDoc name) {
1263        // We can't use the standard dispatchAggregate, since it assumes
1264
// that the Attr is still attached to an owner. This code is
1265
// similar but dispatches to the previous owner, "element".
1266
if (mutationEvents) {
1267            // If we have to send DOMAttrModified (determined earlier),
1268
// do so.
1269
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1270            if (lc.total > 0) {
1271                MutationEventImpl me= new MutationEventImpl();
1272                me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
1273                                     true, false, attr,
1274                                     attr.getNodeValue(), null, name,
1275                                     MutationEvent.REMOVAL);
1276                dispatchEvent(oldOwner, me);
1277            }
1278
1279            // We can hand off to process DOMSubtreeModified, though.
1280
// Note that only the Element needs to be informed; the
1281
// Attr's subtree has not been changed by this operation.
1282
dispatchAggregateEvents(oldOwner, null, null, (short) 0);
1283        }
1284    }
1285    
1286
1287    /**
1288     * A method to be called when an attribute node has been renamed
1289     */

1290    void renamedAttrNode(Attr JavaDoc oldAt, Attr JavaDoc newAt) {
1291    // REVISIT: To be implemented!!!
1292
}
1293
1294    /**
1295     * A method to be called when an element has been renamed
1296     */

1297    void renamedElement(Element JavaDoc oldEl, Element JavaDoc newEl) {
1298    // REVISIT: To be implemented!!!
1299
}
1300
1301} // class DocumentImpl
1302
Popular Tags