KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2001 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package org.enhydra.apache.xerces.dom;
59
60 import java.util.Enumeration JavaDoc;
61 import java.util.Hashtable JavaDoc;
62 import java.util.Vector JavaDoc;
63
64 import org.enhydra.apache.xerces.dom.events.EventImpl;
65 import org.enhydra.apache.xerces.dom.events.MutationEventImpl;
66 import org.w3c.dom.DOMException JavaDoc;
67 import org.w3c.dom.DOMImplementation JavaDoc;
68 import org.w3c.dom.DocumentType JavaDoc;
69 import org.w3c.dom.NamedNodeMap JavaDoc;
70 import org.w3c.dom.Node JavaDoc;
71 import org.w3c.dom.events.DocumentEvent JavaDoc;
72 import org.w3c.dom.events.Event JavaDoc;
73 import org.w3c.dom.events.EventException JavaDoc;
74 import org.w3c.dom.events.EventListener JavaDoc;
75 import org.w3c.dom.events.MutationEvent JavaDoc;
76 import org.w3c.dom.ranges.DocumentRange;
77 import org.w3c.dom.ranges.Range;
78 import org.w3c.dom.traversal.DocumentTraversal;
79 import org.w3c.dom.traversal.NodeFilter;
80 import org.w3c.dom.traversal.NodeIterator;
81 import org.w3c.dom.traversal.TreeWalker;
82
83
84 /**
85  * The Document interface represents the entire HTML or XML document.
86  * Conceptually, it is the root of the document tree, and provides the
87  * primary access to the document's data.
88  * <P>
89  * Since elements, text nodes, comments, processing instructions,
90  * etc. cannot exist outside the context of a Document, the Document
91  * interface also contains the factory methods needed to create these
92  * objects. The Node objects created have a ownerDocument attribute
93  * which associates them with the Document within whose context they
94  * were created.
95  * <p>
96  * The DocumentImpl class also implements the DOM Level 2 DocumentTraversal
97  * interface. This interface is comprised of factory methods needed to
98  * create NodeIterators and TreeWalkers. The process of creating NodeIterator
99  * objects also adds these references to this document.
100  * After finishing with an iterator it is important to remove the object
101  * using the remove methods in this implementation. This allows the release of
102  * the references from the iterator objects to the DOM Nodes.
103  * <p>
104  * <b>Note:</b> When any node in the document is serialized, the
105  * entire document is serialized along with it.
106  *
107  * @author Arnaud Le Hors, IBM
108  * @author Joe Kesselman, IBM
109  * @author Andy Clark, IBM
110  * @author Ralf Pfeiffer, IBM
111  * @version
112  * @since PR-DOM-Level-1-19980818.
113  */

114 public class DocumentImpl
115     extends CoreDocumentImpl
116     implements DocumentTraversal, DocumentEvent JavaDoc, DocumentRange {
117
118     //
119
// Constants
120
//
121

122     /** Serialization version. */
123     static final long serialVersionUID = 515687835542616694L;
124
125     //
126
// Data
127
//
128

129     /** Iterators */
130     // REVISIT: Should this be transient? -Ac
131
protected Vector JavaDoc iterators;
132
133      /** Ranges */
134     // REVISIT: Should this be transient? -Ac
135
protected Vector JavaDoc ranges;
136
137     /** Table for user data attached to this document nodes. */
138     protected Hashtable JavaDoc userData;
139
140     /** Table for event listeners registered to this document nodes. */
141     protected Hashtable JavaDoc eventListeners;
142
143     /** Bypass mutation events firing. */
144     protected boolean mutationEvents = false;
145
146     //
147
// Constructors
148
//
149

150     /**
151      * NON-DOM: Actually creating a Document is outside the DOM's spec,
152      * since it has to operate in terms of a particular implementation.
153      */

154     public DocumentImpl() {
155         super();
156     }
157
158     /** Constructor. */
159     public DocumentImpl(boolean grammarAccess) {
160         super(grammarAccess);
161     }
162
163     /**
164      * For DOM2 support.
165      * The createDocument factory method is in DOMImplementation.
166      */

167     public DocumentImpl(DocumentType JavaDoc doctype)
168     {
169         super(doctype);
170     }
171
172     /** For DOM2 support. */
173     public DocumentImpl(DocumentType JavaDoc doctype, boolean grammarAccess) {
174         super(doctype, grammarAccess);
175     }
176
177     //
178
// Node methods
179
//
180

181     /**
182      * Deep-clone a document, including fixing ownerDoc for the cloned
183      * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
184      * protection. I've chosen to implement it by calling importNode
185      * which is DOM Level 2.
186      *
187      * @return org.w3c.dom.Node
188      * @param deep boolean, iff true replicate children
189      */

190     public Node JavaDoc cloneNode(boolean deep) {
191
192         DocumentImpl newdoc = new DocumentImpl();
193         cloneNode(newdoc, deep);
194
195         // experimental
196
newdoc.mutationEvents = mutationEvents;
197
198         return newdoc;
199
200     } // cloneNode(boolean):Node
201

202     /**
203      * Retrieve information describing the abilities of this particular
204      * DOM implementation. Intended to support applications that may be
205      * using DOMs retrieved from several different sources, potentially
206      * with different underlying representations.
207      */

208     public DOMImplementation JavaDoc getImplementation() {
209         // Currently implemented as a singleton, since it's hardcoded
210
// information anyway.
211
return DOMImplementationImpl.getDOMImplementation();
212     }
213
214     /**
215      * Store user data related to a given node
216      * This is a place where we could use weak references! Indeed, the node
217      * here won't be GC'ed as long as some user data is attached to it, since
218      * the userData table will have a reference to the node.
219      */

220     protected void setUserData(NodeImpl n, Object JavaDoc data) {
221         if (userData == null) {
222             userData = new Hashtable JavaDoc();
223         }
224         if (data == null) {
225             userData.remove(n);
226         } else {
227             userData.put(n, data);
228         }
229     }
230
231     /**
232      * Retreive user data related to a given node
233      */

234     protected Object JavaDoc getUserData(NodeImpl n) {
235         if (userData == null) {
236             return null;
237         }
238         return userData.get(n);
239     }
240
241     //
242
// DocumentTraversal methods
243
//
244

245     /**
246      * NON-DOM extension:
247      * Create and return a NodeIterator. The NodeIterator is
248      * added to a list of NodeIterators so that it can be
249      * removed to free up the DOM Nodes it references.
250      * @see #removeNodeIterator
251      * @see #removeNodeIterators
252      *
253      * @param root The root of the iterator.
254      * @param whatToShow The whatToShow mask.
255      * @param filter The NodeFilter installed. Null means no filter.
256      */

257     public NodeIterator createNodeIterator(Node JavaDoc root,
258                                            short whatToShow,
259                                            NodeFilter filter)
260     {
261         return createNodeIterator(root, whatToShow, filter, true);
262     }
263
264     /**
265      * Create and return a NodeIterator. The NodeIterator is
266      * added to a list of NodeIterators so that it can be
267      * removed to free up the DOM Nodes it references.
268      * @see #removeNodeIterator
269      * @see #removeNodeIterators
270      *
271      * @param root The root of the iterator.
272      * @param whatToShow The whatToShow mask.
273      * @param filter The NodeFilter installed. Null means no filter.
274      * @param entityReferenceExpansion true to expand the contents of
275      * EntityReference nodes
276      * @since WD-DOM-Level-2-19990923
277      */

278     public NodeIterator createNodeIterator(Node JavaDoc root,
279                                            int whatToShow,
280                                            NodeFilter filter,
281                                            boolean entityReferenceExpansion)
282     {
283         NodeIterator iterator = new NodeIteratorImpl(this,
284                                                      root,
285                                                      whatToShow,
286                                                      filter,
287                                                      entityReferenceExpansion);
288         if (iterators == null) {
289             iterators = new Vector JavaDoc();
290         }
291
292         iterators.addElement(iterator);
293
294         return iterator;
295     }
296
297     /**
298      * NON-DOM extension:
299      * Create and return a TreeWalker.
300      *
301      * @param root The root of the iterator.
302      * @param whatToShow The whatToShow mask.
303      * @param filter The NodeFilter installed. Null means no filter.
304      */

305     public TreeWalker createTreeWalker(Node JavaDoc root,
306                                        short whatToShow,
307                                        NodeFilter filter)
308     {
309         return createTreeWalker(root, whatToShow, filter, true);
310     }
311     /**
312      * Create and return a TreeWalker.
313      *
314      * @param root The root of the iterator.
315      * @param whatToShow The whatToShow mask.
316      * @param filter The NodeFilter installed. Null means no filter.
317      * @param entityReferenceExpansion true to expand the contents of
318      * EntityReference nodes
319      * @since WD-DOM-Level-2-19990923
320      */

321     public TreeWalker createTreeWalker(Node JavaDoc root,
322                                        int whatToShow,
323                                        NodeFilter filter,
324                                        boolean entityReferenceExpansion)
325     {
326         if (root == null) {
327             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR,
328                                    "DOM007 Not supported");
329         }
330         return new TreeWalkerImpl(root, whatToShow, filter,
331                                   entityReferenceExpansion);
332     }
333
334     //
335
// Not DOM Level 2. Support DocumentTraversal methods.
336
//
337

338     /** This is not called by the developer client. The
339      * developer client uses the detach() function on the
340      * NodeIterator itself. <p>
341      *
342      * This function is called from the NodeIterator#detach().
343      */

344      void removeNodeIterator(NodeIterator nodeIterator) {
345
346         if (nodeIterator == null) return;
347         if (iterators == null) return;
348
349         iterators.removeElement(nodeIterator);
350     }
351
352     //
353
// DocumentRange methods
354
//
355
/**
356      */

357     public Range createRange() {
358
359         if (ranges == null) {
360             ranges = new Vector JavaDoc();
361         }
362
363         Range range = new RangeImpl(this);
364
365         ranges.addElement(range);
366
367         return range;
368
369     }
370
371     /** Not a client function. Called by Range.detach(),
372      * so a Range can remove itself from the list of
373      * Ranges.
374      */

375     void removeRange(Range range) {
376
377         if (range == null) return;
378         if (ranges == null) return;
379
380         ranges.removeElement(range);
381     }
382
383     /**
384      * A method to be called when some text was changed in a text node,
385      * so that live objects can be notified.
386      */

387     void replacedText(NodeImpl node) {
388         // notify ranges
389
if (ranges != null) {
390             Enumeration JavaDoc enumer = ranges.elements();
391             while (enumer.hasMoreElements()) {
392                 ((RangeImpl)enumer.nextElement()).receiveReplacedText(node);
393             }
394         }
395     }
396
397     /**
398      * A method to be called when some text was deleted from a text node,
399      * so that live objects can be notified.
400      */

401     void deletedText(NodeImpl node, int offset, int count) {
402         // notify ranges
403
if (ranges != null) {
404             Enumeration JavaDoc enumer = ranges.elements();
405             while (enumer.hasMoreElements()) {
406                 ((RangeImpl)enumer.nextElement()).receiveDeletedText(node,
407                                                                    offset,
408                                                                    count);
409             }
410         }
411     }
412
413     /**
414      * A method to be called when some text was inserted into a text node,
415      * so that live objects can be notified.
416      */

417     void insertedText(NodeImpl node, int offset, int count) {
418         // notify ranges
419
if (ranges != null) {
420             Enumeration JavaDoc enumer = ranges.elements();
421             while (enumer.hasMoreElements()) {
422                 ((RangeImpl)enumer.nextElement()).receiveInsertedText(node,
423                                                                     offset,
424                                                                     count);
425             }
426         }
427     }
428
429     /**
430      * A method to be called when a text node has been split,
431      * so that live objects can be notified.
432      */

433     void splitData(Node JavaDoc node, Node JavaDoc newNode, int offset) {
434         // notify ranges
435
if (ranges != null) {
436             Enumeration JavaDoc enumer = ranges.elements();
437             while (enumer.hasMoreElements()) {
438                 ((RangeImpl)enumer.nextElement()).receiveSplitData(node,
439                                                                  newNode,
440                                                                  offset);
441             }
442         }
443     }
444
445     //
446
// DocumentEvent methods
447
//
448

449     /**
450      * Introduced in DOM Level 2. Optional. <p>
451      * Create and return Event objects.
452      *
453      * @param type The eventType parameter specifies the type of Event
454      * interface to be created. If the Event interface specified is supported
455      * by the implementation this method will return a new Event of the
456      * interface type requested. If the Event is to be dispatched via the
457      * dispatchEvent method the appropriate event init method must be called
458      * after creation in order to initialize the Event's values. As an
459      * example, a user wishing to synthesize some kind of Event would call
460      * createEvent with the parameter "Events". The initEvent method could then
461      * be called on the newly created Event to set the specific type of Event
462      * to be dispatched and set its context information.
463      * @return Newly created Event
464      * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
465      * does not support the type of Event interface requested
466      * @since WD-DOM-Level-2-19990923
467      */

468     public Event JavaDoc createEvent(String JavaDoc type)
469     throws DOMException JavaDoc {
470         if (type.equalsIgnoreCase("Events") || "Event".equals(type))
471             return new EventImpl();
472         if (type.equalsIgnoreCase("MutationEvents") ||
473                 "MutationEvent".equals(type))
474             return new MutationEventImpl();
475         else
476             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR,
477                        "DOM007 Not supported");
478     }
479
480     /**
481      * Sets whether the DOM implementation generates mutation events
482      * upon operations.
483      */

484     void setMutationEvents(boolean set) {
485         mutationEvents = set;
486     }
487
488     /**
489      * Returns true if the DOM implementation generates mutation events.
490      */

491     boolean getMutationEvents() {
492         return mutationEvents;
493     }
494
495     /**
496      * Store event listener registered on a given node
497      * This is another place where we could use weak references! Indeed, the
498      * node here won't be GC'ed as long as some listener is registered on it,
499      * since the eventsListeners table will have a reference to the node.
500      */

501     protected void setEventListeners(NodeImpl n, Vector JavaDoc listeners) {
502         if (eventListeners == null) {
503             eventListeners = new Hashtable JavaDoc();
504         }
505         if (listeners == null) {
506             eventListeners.remove(n);
507             if (eventListeners.isEmpty()) {
508                 // stop firing events when there isn't any listener
509
mutationEvents = false;
510             }
511         } else {
512             eventListeners.put(n, listeners);
513             // turn mutation events on
514
mutationEvents = true;
515         }
516     }
517
518     /**
519      * Retreive event listener registered on a given node
520      */

521     protected Vector JavaDoc getEventListeners(NodeImpl n) {
522         if (eventListeners == null) {
523             return null;
524         }
525         return (Vector JavaDoc) eventListeners.get(n);
526     }
527
528     //
529
// EventTarget support (public and internal)
530
//
531

532     //
533
// Constants
534
//
535

536     /*
537      * NON-DOM INTERNAL: Class LEntry is just a struct used to represent
538      * event listeners registered with this node. Copies of this object
539      * are hung from the nodeListeners Vector.
540      * <p>
541      * I considered using two vectors -- one for capture,
542      * one for bubble -- but decided that since the list of listeners
543      * is probably short in most cases, it might not be worth spending
544      * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
545      */

546     class LEntry
547     {
548         String JavaDoc type;
549         EventListener JavaDoc listener;
550         boolean useCapture;
551         
552         /** NON-DOM INTERNAL: Constructor for Listener list Entry
553          * @param type Event name (NOT event group!) to listen for.
554          * @param listener Who gets called when event is dispatched
555          * @param useCaptue True iff listener is registered on
556          * capturing phase rather than at-target or bubbling
557          */

558         LEntry(String JavaDoc type, EventListener JavaDoc listener, boolean useCapture)
559         {
560             this.type = type;
561             this.listener = listener;
562             this.useCapture = useCapture;
563         }
564     } // LEntry
565

566     /**
567      * Introduced in DOM Level 2. <p> Register an event listener with this
568      * Node. A listener may be independently registered as both Capturing and
569      * Bubbling, but may only be registered once per role; redundant
570      * registrations are ignored.
571      * @param node node to add listener to
572      * @param type Event name (NOT event group!) to listen for.
573      * @param listener Who gets called when event is dispatched
574      * @param useCapture True iff listener is registered on
575      * capturing phase rather than at-target or bubbling
576      */

577     protected void addEventListener(NodeImpl node, String JavaDoc type,
578                                     EventListener JavaDoc listener, boolean useCapture)
579     {
580         // We can't dispatch to blank type-name, and of course we need
581
// a listener to dispatch to
582
if (type == null || type.equals("") || listener == null)
583             return;
584
585         // Each listener may be registered only once per type per phase.
586
// Simplest way to code that is to zap the previous entry, if any.
587
removeEventListener(node, type, listener, useCapture);
588         
589         Vector JavaDoc nodeListeners = getEventListeners(node);
590         if(nodeListeners == null) {
591             nodeListeners = new Vector JavaDoc();
592             setEventListeners(node, nodeListeners);
593         }
594         nodeListeners.addElement(new LEntry(type, listener, useCapture));
595         
596         // Record active listener
597
LCount lc = LCount.lookup(type);
598         if (useCapture)
599             ++lc.captures;
600         else
601             ++lc.bubbles;
602
603     } // addEventListener(NodeImpl,String,EventListener,boolean) :void
604

605     /**
606      * Introduced in DOM Level 2. <p> Deregister an event listener previously
607      * registered with this Node. A listener must be independently removed
608      * from the Capturing and Bubbling roles. Redundant removals (of listeners
609      * not currently registered for this role) are ignored.
610      * @param node node to remove listener from
611      * @param type Event name (NOT event group!) to listen for.
612      * @param listener Who gets called when event is dispatched
613      * @param useCapture True iff listener is registered on
614      * capturing phase rather than at-target or bubbling
615      */

616     protected void removeEventListener(NodeImpl node, String JavaDoc type,
617                                        EventListener JavaDoc listener,
618                                        boolean useCapture)
619     {
620         // If this couldn't be a valid listener registration, ignore request
621
if (type == null || type.equals("") || listener == null)
622             return;
623         Vector JavaDoc nodeListeners = getEventListeners(node);
624         if (nodeListeners == null)
625             return;
626
627         // Note that addListener has previously ensured that
628
// each listener may be registered only once per type per phase.
629
// count-down is OK for deletions!
630
for (int i = nodeListeners.size() - 1; i >= 0; --i) {
631             LEntry le = (LEntry) nodeListeners.elementAt(i);
632             if (le.useCapture == useCapture && le.listener == listener &&
633                 le.type.equals(type)) {
634                 nodeListeners.removeElementAt(i);
635                 // Storage management: Discard empty listener lists
636
if (nodeListeners.size() == 0)
637                     setEventListeners(node, null);
638
639                 // Remove active listener
640
LCount lc = LCount.lookup(type);
641                 if (useCapture)
642                     --lc.captures;
643                 else
644                     --lc.bubbles;
645
646                 break; // Found it; no need to loop farther.
647
}
648         }
649     } // removeEventListener(NodeImpl,String,EventListener,boolean) :void
650

651     /**
652      * Introduced in DOM Level 2. <p>
653      * Distribution engine for DOM Level 2 Events.
654      * <p>
655      * Event propagation runs as follows:
656      * <ol>
657      * <li>Event is dispatched to a particular target node, which invokes
658      * this code. Note that the event's stopPropagation flag is
659      * cleared when dispatch begins; thereafter, if it has
660      * been set before processing of a node commences, we instead
661      * immediately advance to the DEFAULT phase.
662      * <li>The node's ancestors are established as destinations for events.
663      * For capture and bubble purposes, node ancestry is determined at
664      * the time dispatch starts. If an event handler alters the document
665      * tree, that does not change which nodes will be informed of the event.
666      * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for
667      * Capturing listeners. If found, they are invoked (see below).
668      * <li>AT_TARGET:
669      * Event is dispatched to NON-CAPTURING listeners on the
670      * target node. Note that capturing listeners on this node are _not_
671      * invoked.
672      * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for
673      * non-capturing listeners.
674      * <li>Default processing: Some DOMs have default behaviors bound to
675      * specific nodes. If this DOM does, and if the event's preventDefault
676      * flag has not been set, we now return to the target node and process
677      * its default handler for this event, if any.
678      * </ol>
679      * <p>
680      * Note that registration of handlers during processing of an event does
681      * not take effect during this phase of this event; they will not be called
682      * until the next time this node is visited by dispatchEvent. On the other
683      * hand, removals take effect immediately.
684      * <p>
685      * If an event handler itself causes events to be dispatched, they are
686      * processed synchronously, before processing resumes
687      * on the event which triggered them. Please be aware that this may
688      * result in events arriving at listeners "out of order" relative
689      * to the actual sequence of requests.
690      * <p>
691      * Note that our implementation resets the event's stop/prevent flags
692      * when dispatch begins.
693      * I believe the DOM's intent is that event objects be redispatchable,
694      * though it isn't stated in those terms.
695      * @param node node to dispatch to
696      * @param event the event object to be dispatched to
697      * registered EventListeners
698      * @return true if the event's <code>preventDefault()</code>
699      * method was invoked by an EventListener; otherwise false.
700     */

701     protected boolean dispatchEvent(NodeImpl node, Event JavaDoc event) {
702         if (event == null) return false;
703         
704         // Can't use anyone else's implementation, since there's no public
705
// API for setting the event's processing-state fields.
706
EventImpl evt = (EventImpl)event;
707
708         // VALIDATE -- must have been initialized at least once, must have
709
// a non-null non-blank name.
710
if(!evt.initialized || evt.type == null || evt.type.equals(""))
711             throw new EventException JavaDoc(EventException.UNSPECIFIED_EVENT_TYPE_ERR,
712                                      "DOM010 Unspecified event type");
713         
714         // If nobody is listening for this event, discard immediately
715
LCount lc = LCount.lookup(evt.getType());
716         if (lc.captures + lc.bubbles + lc.defaults == 0)
717             return evt.preventDefault;
718
719         // INITIALIZE THE EVENT'S DISPATCH STATUS
720
// (Note that Event objects are reusable in our implementation;
721
// that doesn't seem to be explicitly guaranteed in the DOM, but
722
// I believe it is the intent.)
723
evt.target = node;
724         evt.stopPropagation = false;
725         evt.preventDefault = false;
726         
727         // Capture pre-event parentage chain, not including target;
728
// use pre-event-dispatch ancestors even if event handlers mutate
729
// document and change the target's context.
730
// Note that this is parents ONLY; events do not
731
// cross the Attr/Element "blood/brain barrier".
732
// DOMAttrModified. which looks like an exception,
733
// is issued to the Element rather than the Attr
734
// and causes a _second_ DOMSubtreeModified in the Element's
735
// tree.
736
Vector JavaDoc pv = new Vector JavaDoc(10,10);
737         Node JavaDoc p = node;
738         Node JavaDoc n = p.getParentNode();
739         while (n != null) {
740             pv.addElement(n);
741             p = n;
742             n = n.getParentNode();
743         }
744         
745         // CAPTURING_PHASE:
746
if (lc.captures > 0) {
747             evt.eventPhase = Event.CAPTURING_PHASE;
748             // Ancestors are scanned, root to target, for
749
// Capturing listeners.
750
for (int j = pv.size() - 1; j >= 0; --j) {
751                 if (evt.stopPropagation)
752                     break; // Someone set the flag. Phase ends.
753

754                 // Handle all capturing listeners on this node
755
NodeImpl nn = (NodeImpl) pv.elementAt(j);
756                 evt.currentTarget = nn;
757                 Vector JavaDoc nodeListeners = getEventListeners(nn);
758                 if (nodeListeners != null) {
759                     Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
760                     // count-down more efficient
761
for (int i = nl.size() - 1; i >= 0; --i) {
762                         LEntry le = (LEntry) nl.elementAt(i);
763                         if (le.useCapture && le.type.equals(evt.type) &&
764                             nodeListeners.contains(le)) {
765                             try {
766                                 le.listener.handleEvent(evt);
767                             }
768                             catch (Exception JavaDoc e) {
769                                 // All exceptions are ignored.
770
}
771                         }
772                     }
773                 }
774             }
775         }
776         
777         // Both AT_TARGET and BUBBLE use non-capturing listeners.
778
if (lc.bubbles > 0) {
779             // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners
780
// on the target node. Note that capturing listeners on the target
781
// node are _not_ invoked, even during the capture phase.
782
evt.eventPhase = Event.AT_TARGET;
783             evt.currentTarget = node;
784             Vector JavaDoc nodeListeners = getEventListeners(node);
785             if (!evt.stopPropagation && nodeListeners != null) {
786                 Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
787                 // count-down is more efficient
788
for (int i = nl.size() - 1; i >= 0; --i) {
789                     LEntry le = (LEntry) nl.elementAt(i);
790                     if (!le.useCapture && le.type.equals(evt.type) &&
791                         nodeListeners.contains(le)) {
792                         try {
793                             le.listener.handleEvent(evt);
794                         }
795                         catch (Exception JavaDoc e) {
796                             // All exceptions are ignored.
797
}
798                     }
799                 }
800             }
801             // BUBBLING_PHASE: Ancestors are scanned, target to root, for
802
// non-capturing listeners. If the event's preventBubbling flag
803
// has been set before processing of a node commences, we
804
// instead immediately advance to the default phase.
805
// Note that not all events bubble.
806
if (evt.bubbles) {
807                 evt.eventPhase = Event.BUBBLING_PHASE;
808                 for (int j = 0; j < pv.size(); ++j) {
809                     if (evt.stopPropagation)
810                         break; // Someone set the flag. Phase ends.
811

812                     // Handle all bubbling listeners on this node
813
NodeImpl nn = (NodeImpl) pv.elementAt(j);
814                     evt.currentTarget = nn;
815                     nodeListeners = getEventListeners(nn);
816                     if (nodeListeners != null) {
817                         Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
818                         // count-down more efficient
819
for (int i = nl.size() - 1; i >= 0; --i) {
820                             LEntry le = (LEntry) nl.elementAt(i);
821                             if (!le.useCapture && le.type.equals(evt.type) &&
822                                 nodeListeners.contains(le)) {
823                                 try {
824                                     le.listener.handleEvent(evt);
825                                 }
826                                 catch (Exception JavaDoc e) {
827                                     // All exceptions are ignored.
828
}
829                             }
830                         }
831                     }
832                 }
833             }
834         }
835         
836         // DEFAULT PHASE: Some DOMs have default behaviors bound to specific
837
// nodes. If this DOM does, and if the event's preventDefault flag has
838
// not been set, we now return to the target node and process its
839
// default handler for this event, if any.
840
// No specific phase value defined, since this is DOM-internal
841
if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) {
842             // evt.eventPhase = Event.DEFAULT_PHASE;
843
// evt.currentTarget = node;
844
// DO_DEFAULT_OPERATION
845
}
846
847         return evt.preventDefault;
848     } // dispatchEvent(NodeImpl,Event) :boolean
849

850
851     /**
852      * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom...
853      * are dispatched to an entire subtree. This is the distribution code
854      * therefor. They DO NOT bubble, thanks be, but may be captured.
855      * <p>
856      * ***** At the moment I'm being sloppy and using the normal
857      * capture dispatcher on every node. This could be optimized hugely
858      * by writing a capture engine that tracks our position in the tree to
859      * update the capture chain without repeated chases up to root.
860      * @param node node to dispatch to
861      * @param n node which was directly inserted or removed
862      * @param e event to be sent to that node and its subtree
863      */

864     protected void dispatchEventToSubtree(NodeImpl node, Node JavaDoc n, Event JavaDoc e) {
865         Vector JavaDoc nodeListeners = getEventListeners(node);
866         if (nodeListeners == null || n == null)
867             return;
868
869         // ***** Recursive implementation. This is excessively expensive,
870
// and should be replaced in conjunction with optimization
871
// mentioned above.
872
((NodeImpl) n).dispatchEvent(e);
873         if (n.getNodeType() == Node.ELEMENT_NODE) {
874             NamedNodeMap JavaDoc a = n.getAttributes();
875             for (int i = a.getLength() - 1; i >= 0; --i)
876                 dispatchEventToSubtree(node, a.item(i), e);
877         }
878         dispatchEventToSubtree(node, n.getFirstChild(), e);
879         dispatchEventToSubtree(node, n.getNextSibling(), e);
880     } // dispatchEventToSubtree(NodeImpl,Node,Event) :void
881

882     /**
883      * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
884      * (two values, the Attr node affected (if any) and its previous
885      * string value. Simple struct, no methods.
886      */

887     class EnclosingAttr
888     {
889         AttrImpl node;
890         String JavaDoc oldvalue;
891     }
892
893     EnclosingAttr savedEnclosingAttr;
894
895     /**
896      * NON-DOM INTERNAL: Convenience wrapper for calling
897      * dispatchAggregateEvents when the context was established
898      * by <code>savedEnclosingAttr</code>.
899      * @param node node to dispatch to
900      * @param ea description of Attr affected by current operation
901      */

902     protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) {
903         if (ea != null)
904             dispatchAggregateEvents(node, ea.node, ea.oldvalue,
905                                     MutationEvent.MODIFICATION);
906         else
907             dispatchAggregateEvents(node, null, null, (short) 0);
908             
909     } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
910

911     /**
912      * NON-DOM INTERNAL: Generate the "aggregated" post-mutation events
913      * DOMAttrModified and DOMSubtreeModified.
914      * Both of these should be issued only once for each user-requested
915      * mutation operation, even if that involves multiple changes to
916      * the DOM.
917      * For example, if a DOM operation makes multiple changes to a single
918      * Attr before returning, it would be nice to generate only one
919      * DOMAttrModified, and multiple changes over larger scope but within
920      * a recognizable single subtree might want to generate only one
921      * DOMSubtreeModified, sent to their lowest common ancestor.
922      * <p>
923      * To manage this, use the "internal" versions of insert and remove
924      * with MUTATION_LOCAL, then make an explicit call to this routine
925      * at the higher level. Some examples now exist in our code.
926      *
927      * @param node The node to dispatch to
928      * @param enclosingAttr The Attr node (if any) whose value has been changed
929      * as a result of the DOM operation. Null if none such.
930      * @param oldValue The String value previously held by the
931      * enclosingAttr. Ignored if none such.
932      * @param change Type of modification to the attr. See
933      * MutationEvent.attrChange
934      */

935     protected void dispatchAggregateEvents(NodeImpl node,
936                                            AttrImpl enclosingAttr,
937                                            String JavaDoc oldvalue, short change) {
938         // We have to send DOMAttrModified.
939
NodeImpl owner = null;
940         if (enclosingAttr != null) {
941             LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
942             owner = (NodeImpl) enclosingAttr.getOwnerElement();
943             if (lc.captures + lc.bubbles + lc.defaults > 0) {
944                 if (owner != null) {
945                     MutationEventImpl me = new MutationEventImpl();
946                     me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
947                                          true, false, enclosingAttr,
948                                          oldvalue,
949                                          enclosingAttr.getNodeValue(),
950                                          enclosingAttr.getNodeName(),
951                                          change);
952                     owner.dispatchEvent(me);
953                 }
954             }
955         }
956         // DOMSubtreeModified gets sent to the lowest common root of a
957
// set of changes.
958
// "This event is dispatched after all other events caused by the
959
// mutation have been fired."
960
LCount lc = LCount.lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED);
961         if (lc.captures + lc.bubbles + lc.defaults > 0) {
962             MutationEvent JavaDoc me = new MutationEventImpl();
963             me.initMutationEvent(MutationEventImpl.DOM_SUBTREE_MODIFIED,
964                                  true, false, null, null,
965                                  null, null, (short) 0);
966
967             // If we're within an Attr, DStM gets sent to the Attr
968
// and to its owningElement. Otherwise we dispatch it
969
// locally.
970
if (enclosingAttr != null) {
971                 dispatchEvent(enclosingAttr, me);
972                 if (owner != null)
973                     dispatchEvent(owner, me);
974             }
975             else
976                 dispatchEvent(node, me);
977         }
978     } // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void
979

980     /**
981      * NON-DOM INTERNAL: Pre-mutation context check, in
982      * preparation for later generating DOMAttrModified events.
983      * Determines whether this node is within an Attr
984      * @param node node to get enclosing attribute for
985      * @return either a description of that Attr, or null if none such.
986      */

987     protected void saveEnclosingAttr(NodeImpl node) {
988         savedEnclosingAttr = null;
989         // MUTATION PREPROCESSING AND PRE-EVENTS:
990
// If we're within the scope of an Attr and DOMAttrModified
991
// was requested, we need to preserve its previous value for
992
// that event.
993
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
994         if (lc.captures + lc.bubbles + lc.defaults > 0) {
995             NodeImpl eventAncestor = node;
996             while (true) {
997                 if (eventAncestor == null)
998                     return;
999                 int type = eventAncestor.getNodeType();
1000                if (type == Node.ATTRIBUTE_NODE) {
1001                    EnclosingAttr retval = new EnclosingAttr();
1002                    retval.node = (AttrImpl) eventAncestor;
1003                    retval.oldvalue = retval.node.getNodeValue();
1004                    savedEnclosingAttr = retval;
1005                    return;
1006                }
1007                else if (type == Node.ENTITY_REFERENCE_NODE)
1008                    eventAncestor = eventAncestor.parentNode();
1009                else
1010                    return;
1011                // Any other parent means we're not in an Attr
1012
}
1013        }
1014    } // saveEnclosingAttr(NodeImpl) :void
1015

1016    /**
1017     * A method to be called when a character data node has been modified
1018     */

1019    void modifyingCharacterData(NodeImpl node) {
1020        if (mutationEvents) {
1021            saveEnclosingAttr(node);
1022        }
1023    }
1024
1025    /**
1026     * A method to be called when a character data node has been modified
1027     */

1028    void modifiedCharacterData(NodeImpl node, String JavaDoc oldvalue, String JavaDoc value) {
1029        if (mutationEvents) {
1030            // MUTATION POST-EVENTS:
1031
LCount lc =
1032                LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
1033            if (lc.captures + lc.bubbles + lc.defaults > 0) {
1034                MutationEvent JavaDoc me = new MutationEventImpl();
1035                me.initMutationEvent(
1036                                 MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1037                                     true, false, null,
1038                                     oldvalue, value, null, (short) 0);
1039                dispatchEvent(me);
1040            }
1041            
1042            // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1043
// if required. (Common to most kinds of mutation)
1044
dispatchAggregateEvents(node, savedEnclosingAttr);
1045        } // End mutation postprocessing
1046
}
1047
1048    /**
1049     * A method to be called when a node is about to be inserted in the tree.
1050     */

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

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

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

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

1200    void replacingNode(NodeImpl node) {
1201        if (mutationEvents) {
1202            saveEnclosingAttr(node);
1203        }
1204    }
1205
1206    /**
1207     * A method to be called when a node has been replaced in the tree.
1208     */

1209    void replacedNode(NodeImpl node) {
1210        if (mutationEvents) {
1211            dispatchAggregateEvents(node, savedEnclosingAttr);
1212        }
1213    }
1214
1215    /**
1216     * A method to be called when an attribute value has been modified
1217     */

1218    void modifiedAttrValue(AttrImpl attr, String JavaDoc oldvalue) {
1219        if (mutationEvents) {
1220            // MUTATION POST-EVENTS:
1221
dispatchAggregateEvents(attr, attr, oldvalue,
1222                                    MutationEvent.MODIFICATION);
1223        }
1224    }
1225
1226    /**
1227     * A method to be called when an attribute node has been set
1228     */

1229    void setAttrNode(AttrImpl attr, AttrImpl previous) {
1230        if (mutationEvents) {
1231            // MUTATION POST-EVENTS:
1232
if (previous == null) {
1233                dispatchAggregateEvents(attr.ownerNode, attr, null,
1234                                        MutationEvent.ADDITION);
1235            }
1236            else {
1237                dispatchAggregateEvents(attr.ownerNode, attr,
1238                                        previous.getNodeValue(),
1239                                        MutationEvent.MODIFICATION);
1240            }
1241        }
1242    }
1243
1244    /**
1245     * A method to be called when an attribute node has been removed
1246     */

1247    void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String JavaDoc name) {
1248        // We can't use the standard dispatchAggregate, since it assumes
1249
// that the Attr is still attached to an owner. This code is
1250
// similar but dispatches to the previous owner, "element".
1251
if (mutationEvents) {
1252            // If we have to send DOMAttrModified (determined earlier),
1253
// do so.
1254
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1255            if (lc.captures + lc.bubbles + lc.defaults > 0) {
1256                MutationEventImpl me= new MutationEventImpl();
1257                me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
1258                                     true, false, null,
1259                                     attr.getNodeValue(), null, name,
1260                                     MutationEvent.REMOVAL);
1261                dispatchEvent(oldOwner, me);
1262            }
1263
1264            // We can hand off to process DOMSubtreeModified, though.
1265
// Note that only the Element needs to be informed; the
1266
// Attr's subtree has not been changed by this operation.
1267
dispatchAggregateEvents(oldOwner, null, null, (short) 0);
1268        }
1269    }
1270
1271} // class DocumentImpl
1272
Popular Tags