KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > dom > DocumentImpl


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

117 public class DocumentImpl
118     extends CoreDocumentImpl
119     implements DocumentTraversal, DocumentEvent JavaDoc, DocumentRange {
120
121     //
122
// Constants
123
//
124

125     /** Serialization version. */
126     static final long serialVersionUID = 515687835542616694L;
127
128     //
129
// Data
130
//
131

132     /** Iterators */
133     // REVISIT: Should this be transient? -Ac
134
protected Vector JavaDoc iterators;
135
136      /** Ranges */
137     // REVISIT: Should this be transient? -Ac
138
protected Vector JavaDoc ranges;
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         callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
194         cloneNode(newdoc, deep);
195
196         // experimental
197
newdoc.mutationEvents = mutationEvents;
198
199         return newdoc;
200
201     } // cloneNode(boolean):Node
202

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

209     public DOMImplementation JavaDoc getImplementation() {
210         // Currently implemented as a singleton, since it's hardcoded
211
// information anyway.
212
return DOMImplementationImpl.getDOMImplementation();
213     }
214
215     //
216
// DocumentTraversal methods
217
//
218

219     /**
220      * NON-DOM extension:
221      * Create and return a NodeIterator. The NodeIterator is
222      * added to a list of NodeIterators so that it can be
223      * removed to free up the DOM Nodes it references.
224      *
225      * @param root The root of the iterator.
226      * @param whatToShow The whatToShow mask.
227      * @param filter The NodeFilter installed. Null means no filter.
228      */

229     public NodeIterator createNodeIterator(Node JavaDoc root,
230                                            short whatToShow,
231                                            NodeFilter filter)
232     {
233         return createNodeIterator(root, whatToShow, filter, true);
234     }
235
236     /**
237      * Create and return a NodeIterator. The NodeIterator is
238      * added to a list of NodeIterators so that it can be
239      * removed to free up the DOM Nodes it references.
240      *
241      * @param root The root of the iterator.
242      * @param whatToShow The whatToShow mask.
243      * @param filter The NodeFilter installed. Null means no filter.
244      * @param entityReferenceExpansion true to expand the contents of
245      * EntityReference nodes
246      * @since WD-DOM-Level-2-19990923
247      */

248     public NodeIterator createNodeIterator(Node JavaDoc root,
249                                            int whatToShow,
250                                            NodeFilter filter,
251                                            boolean entityReferenceExpansion)
252     {
253         
254         if (root == null) {
255                   String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
256                   throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
257         }
258
259         NodeIterator iterator = new NodeIteratorImpl(this,
260                                                      root,
261                                                      whatToShow,
262                                                      filter,
263                                                      entityReferenceExpansion);
264         if (iterators == null) {
265             iterators = new Vector JavaDoc();
266         }
267
268         iterators.addElement(iterator);
269
270         return iterator;
271     }
272
273     /**
274      * NON-DOM extension:
275      * Create and return a TreeWalker.
276      *
277      * @param root The root of the iterator.
278      * @param whatToShow The whatToShow mask.
279      * @param filter The NodeFilter installed. Null means no filter.
280      */

281     public TreeWalker createTreeWalker(Node JavaDoc root,
282                                        short whatToShow,
283                                        NodeFilter filter)
284     {
285         return createTreeWalker(root, whatToShow, filter, true);
286     }
287     /**
288      * Create and return a TreeWalker.
289      *
290      * @param root The root of the iterator.
291      * @param whatToShow The whatToShow mask.
292      * @param filter The NodeFilter installed. Null means no filter.
293      * @param entityReferenceExpansion true to expand the contents of
294      * EntityReference nodes
295      * @since WD-DOM-Level-2-19990923
296      */

297     public TreeWalker createTreeWalker(Node JavaDoc root,
298                                        int whatToShow,
299                                        NodeFilter filter,
300                                        boolean entityReferenceExpansion)
301     {
302         if (root == null) {
303             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
304             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
305         }
306         return new TreeWalkerImpl(root, whatToShow, filter,
307                                   entityReferenceExpansion);
308     }
309
310     //
311
// Not DOM Level 2. Support DocumentTraversal methods.
312
//
313

314     /** This is not called by the developer client. The
315      * developer client uses the detach() function on the
316      * NodeIterator itself. <p>
317      *
318      * This function is called from the NodeIterator#detach().
319      */

320      void removeNodeIterator(NodeIterator nodeIterator) {
321
322         if (nodeIterator == null) return;
323         if (iterators == null) return;
324
325         iterators.removeElement(nodeIterator);
326     }
327
328     //
329
// DocumentRange methods
330
//
331
/**
332      */

333     public Range createRange() {
334
335         if (ranges == null) {
336             ranges = new Vector JavaDoc();
337         }
338
339         Range range = new RangeImpl(this);
340
341         ranges.addElement(range);
342
343         return range;
344
345     }
346
347     /** Not a client function. Called by Range.detach(),
348      * so a Range can remove itself from the list of
349      * Ranges.
350      */

351     void removeRange(Range range) {
352
353         if (range == null) return;
354         if (ranges == null) return;
355
356         ranges.removeElement(range);
357     }
358
359     /**
360      * A method to be called when some text was changed in a text node,
361      * so that live objects can be notified.
362      */

363     void replacedText(NodeImpl node) {
364         // notify ranges
365
if (ranges != null) {
366             int size = ranges.size();
367             for (int i = 0; i != size; i++) {
368                 ((RangeImpl)ranges.elementAt(i)).receiveReplacedText(node);
369             }
370         }
371     }
372
373     /**
374      * A method to be called when some text was deleted from a text node,
375      * so that live objects can be notified.
376      */

377     void deletedText(NodeImpl node, int offset, int count) {
378         // notify ranges
379
if (ranges != null) {
380             int size = ranges.size();
381             for (int i = 0; i != size; i++) {
382                 ((RangeImpl)ranges.elementAt(i)).receiveDeletedText(node,
383                                                                 offset, count);
384             }
385         }
386     }
387
388     /**
389      * A method to be called when some text was inserted into a text node,
390      * so that live objects can be notified.
391      */

392     void insertedText(NodeImpl node, int offset, int count) {
393         // notify ranges
394
if (ranges != null) {
395             int size = ranges.size();
396             for (int i = 0; i != size; i++) {
397                 ((RangeImpl)ranges.elementAt(i)).receiveInsertedText(node,
398                                                                 offset, count);
399             }
400         }
401     }
402
403     /**
404      * A method to be called when a text node has been split,
405      * so that live objects can be notified.
406      */

407     void splitData(Node JavaDoc node, Node JavaDoc newNode, int offset) {
408         // notify ranges
409
if (ranges != null) {
410             int size = ranges.size();
411             for (int i = 0; i != size; i++) {
412                 ((RangeImpl)ranges.elementAt(i)).receiveSplitData(node,
413                                                               newNode, offset);
414             }
415         }
416     }
417
418     //
419
// DocumentEvent methods
420
//
421

422     /**
423      * Introduced in DOM Level 2. Optional. <p>
424      * Create and return Event objects.
425      *
426      * @param type The eventType parameter specifies the type of Event
427      * interface to be created. If the Event interface specified is supported
428      * by the implementation this method will return a new Event of the
429      * interface type requested. If the Event is to be dispatched via the
430      * dispatchEvent method the appropriate event init method must be called
431      * after creation in order to initialize the Event's values. As an
432      * example, a user wishing to synthesize some kind of Event would call
433      * createEvent with the parameter "Events". The initEvent method could then
434      * be called on the newly created Event to set the specific type of Event
435      * to be dispatched and set its context information.
436      * @return Newly created Event
437      * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
438      * does not support the type of Event interface requested
439      * @since WD-DOM-Level-2-19990923
440      */

441     public Event JavaDoc createEvent(String JavaDoc type)
442     throws DOMException JavaDoc {
443         if (type.equalsIgnoreCase("Events") || "Event".equals(type))
444             return new EventImpl();
445         if (type.equalsIgnoreCase("MutationEvents") ||
446                 "MutationEvent".equals(type))
447             return new MutationEventImpl();
448         else {
449             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
450             throw new DOMException JavaDoc(DOMException.NOT_SUPPORTED_ERR, msg);
451         }
452     }
453
454     /**
455      * Sets whether the DOM implementation generates mutation events
456      * upon operations.
457      */

458     void setMutationEvents(boolean set) {
459         mutationEvents = set;
460     }
461
462     /**
463      * Returns true if the DOM implementation generates mutation events.
464      */

465     boolean getMutationEvents() {
466         return mutationEvents;
467     }
468
469     /**
470      * Store event listener registered on a given node
471      * This is another place where we could use weak references! Indeed, the
472      * node here won't be GC'ed as long as some listener is registered on it,
473      * since the eventsListeners table will have a reference to the node.
474      */

475     protected void setEventListeners(NodeImpl n, Vector JavaDoc listeners) {
476         if (eventListeners == null) {
477             eventListeners = new Hashtable JavaDoc();
478         }
479         if (listeners == null) {
480             eventListeners.remove(n);
481             if (eventListeners.isEmpty()) {
482                 // stop firing events when there isn't any listener
483
mutationEvents = false;
484             }
485         } else {
486             eventListeners.put(n, listeners);
487             // turn mutation events on
488
mutationEvents = true;
489         }
490     }
491
492     /**
493      * Retreive event listener registered on a given node
494      */

495     protected Vector JavaDoc getEventListeners(NodeImpl n) {
496         if (eventListeners == null) {
497             return null;
498         }
499         return (Vector JavaDoc) eventListeners.get(n);
500     }
501
502     //
503
// EventTarget support (public and internal)
504
//
505

506     //
507
// Constants
508
//
509

510     /*
511      * NON-DOM INTERNAL: Class LEntry is just a struct used to represent
512      * event listeners registered with this node. Copies of this object
513      * are hung from the nodeListeners Vector.
514      * <p>
515      * I considered using two vectors -- one for capture,
516      * one for bubble -- but decided that since the list of listeners
517      * is probably short in most cases, it might not be worth spending
518      * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
519      */

520     class LEntry implements Serializable JavaDoc
521     {
522         String JavaDoc type;
523         EventListener JavaDoc listener;
524         boolean useCapture;
525         
526         /** NON-DOM INTERNAL: Constructor for Listener list Entry
527          * @param type Event name (NOT event group!) to listen for.
528          * @param listener Who gets called when event is dispatched
529          * @param useCaptue True iff listener is registered on
530          * capturing phase rather than at-target or bubbling
531          */

532         LEntry(String JavaDoc type, EventListener JavaDoc listener, boolean useCapture)
533         {
534             this.type = type;
535             this.listener = listener;
536             this.useCapture = useCapture;
537         }
538
539     } // LEntry
540

541     /**
542      * Introduced in DOM Level 2. <p> Register an event listener with this
543      * Node. A listener may be independently registered as both Capturing and
544      * Bubbling, but may only be registered once per role; redundant
545      * registrations are ignored.
546      * @param node node to add listener to
547      * @param type Event name (NOT event group!) to listen for.
548      * @param listener Who gets called when event is dispatched
549      * @param useCapture True iff listener is registered on
550      * capturing phase rather than at-target or bubbling
551      */

552     protected void addEventListener(NodeImpl node, String JavaDoc type,
553                                     EventListener JavaDoc listener, boolean useCapture)
554     {
555         // We can't dispatch to blank type-name, and of course we need
556
// a listener to dispatch to
557
if (type == null || type.equals("") || listener == null)
558             return;
559
560         // Each listener may be registered only once per type per phase.
561
// Simplest way to code that is to zap the previous entry, if any.
562
removeEventListener(node, type, listener, useCapture);
563         
564         Vector JavaDoc nodeListeners = getEventListeners(node);
565         if(nodeListeners == null) {
566             nodeListeners = new Vector JavaDoc();
567             setEventListeners(node, nodeListeners);
568         }
569         nodeListeners.addElement(new LEntry(type, listener, useCapture));
570         
571         // Record active listener
572
LCount lc = LCount.lookup(type);
573         if (useCapture)
574             ++lc.captures;
575         else
576             ++lc.bubbles;
577
578     } // addEventListener(NodeImpl,String,EventListener,boolean) :void
579

580     /**
581      * Introduced in DOM Level 2. <p> Deregister an event listener previously
582      * registered with this Node. A listener must be independently removed
583      * from the Capturing and Bubbling roles. Redundant removals (of listeners
584      * not currently registered for this role) are ignored.
585      * @param node node to remove listener from
586      * @param type Event name (NOT event group!) to listen for.
587      * @param listener Who gets called when event is dispatched
588      * @param useCapture True iff listener is registered on
589      * capturing phase rather than at-target or bubbling
590      */

591     protected void removeEventListener(NodeImpl node, String JavaDoc type,
592                                        EventListener JavaDoc listener,
593                                        boolean useCapture)
594     {
595         // If this couldn't be a valid listener registration, ignore request
596
if (type == null || type.equals("") || listener == null)
597             return;
598         Vector JavaDoc nodeListeners = getEventListeners(node);
599         if (nodeListeners == null)
600             return;
601
602         // Note that addListener has previously ensured that
603
// each listener may be registered only once per type per phase.
604
// count-down is OK for deletions!
605
for (int i = nodeListeners.size() - 1; i >= 0; --i) {
606             LEntry le = (LEntry) nodeListeners.elementAt(i);
607             if (le.useCapture == useCapture && le.listener == listener &&
608                 le.type.equals(type)) {
609                 nodeListeners.removeElementAt(i);
610                 // Storage management: Discard empty listener lists
611
if (nodeListeners.size() == 0)
612                     setEventListeners(node, null);
613
614                 // Remove active listener
615
LCount lc = LCount.lookup(type);
616                 if (useCapture)
617                     --lc.captures;
618                 else
619                     --lc.bubbles;
620
621                 break; // Found it; no need to loop farther.
622
}
623         }
624     } // removeEventListener(NodeImpl,String,EventListener,boolean) :void
625

626     protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
627         Vector JavaDoc nodeListeners = getEventListeners(src);
628     if (nodeListeners == null) {
629         return;
630     }
631     setEventListeners(tgt, (Vector JavaDoc) nodeListeners.clone());
632     }
633
634     /**
635      * Introduced in DOM Level 2. <p>
636      * Distribution engine for DOM Level 2 Events.
637      * <p>
638      * Event propagation runs as follows:
639      * <ol>
640      * <li>Event is dispatched to a particular target node, which invokes
641      * this code. Note that the event's stopPropagation flag is
642      * cleared when dispatch begins; thereafter, if it has
643      * been set before processing of a node commences, we instead
644      * immediately advance to the DEFAULT phase.
645      * <li>The node's ancestors are established as destinations for events.
646      * For capture and bubble purposes, node ancestry is determined at
647      * the time dispatch starts. If an event handler alters the document
648      * tree, that does not change which nodes will be informed of the event.
649      * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for
650      * Capturing listeners. If found, they are invoked (see below).
651      * <li>AT_TARGET:
652      * Event is dispatched to NON-CAPTURING listeners on the
653      * target node. Note that capturing listeners on this node are _not_
654      * invoked.
655      * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for
656      * non-capturing listeners.
657      * <li>Default processing: Some DOMs have default behaviors bound to
658      * specific nodes. If this DOM does, and if the event's preventDefault
659      * flag has not been set, we now return to the target node and process
660      * its default handler for this event, if any.
661      * </ol>
662      * <p>
663      * Note that registration of handlers during processing of an event does
664      * not take effect during this phase of this event; they will not be called
665      * until the next time this node is visited by dispatchEvent. On the other
666      * hand, removals take effect immediately.
667      * <p>
668      * If an event handler itself causes events to be dispatched, they are
669      * processed synchronously, before processing resumes
670      * on the event which triggered them. Please be aware that this may
671      * result in events arriving at listeners "out of order" relative
672      * to the actual sequence of requests.
673      * <p>
674      * Note that our implementation resets the event's stop/prevent flags
675      * when dispatch begins.
676      * I believe the DOM's intent is that event objects be redispatchable,
677      * though it isn't stated in those terms.
678      * @param node node to dispatch to
679      * @param event the event object to be dispatched to
680      * registered EventListeners
681      * @return true if the event's <code>preventDefault()</code>
682      * method was invoked by an EventListener; otherwise false.
683     */

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

738                 // Handle all capturing listeners on this node
739
NodeImpl nn = (NodeImpl) pv.elementAt(j);
740                 evt.currentTarget = nn;
741                 Vector JavaDoc nodeListeners = getEventListeners(nn);
742                 if (nodeListeners != null) {
743                     Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
744                     // call listeners in the order in which they got registered
745
int nlsize = nl.size();
746                     for (int i = 0; i < nlsize; i++) {
747                         LEntry le = (LEntry) nl.elementAt(i);
748                         if (le.useCapture && le.type.equals(evt.type) &&
749                             nodeListeners.contains(le)) {
750                             try {
751                                 le.listener.handleEvent(evt);
752                             }
753                             catch (Exception JavaDoc e) {
754                                 // All exceptions are ignored.
755
}
756                         }
757                     }
758                 }
759             }
760         }
761         
762         // Both AT_TARGET and BUBBLE use non-capturing listeners.
763
if (lc.bubbles > 0) {
764             // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners
765
// on the target node. Note that capturing listeners on the target
766
// node are _not_ invoked, even during the capture phase.
767
evt.eventPhase = Event.AT_TARGET;
768             evt.currentTarget = node;
769             Vector JavaDoc nodeListeners = getEventListeners(node);
770             if (!evt.stopPropagation && nodeListeners != null) {
771                 Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
772                 // call listeners in the order in which they got registered
773
int nlsize = nl.size();
774                 for (int i = 0; i < nlsize; i++) {
775                     LEntry le = (LEntry) nl.elementAt(i);
776                     if (!le.useCapture && le.type.equals(evt.type) &&
777                         nodeListeners.contains(le)) {
778                         try {
779                             le.listener.handleEvent(evt);
780                         }
781                         catch (Exception JavaDoc e) {
782                             // All exceptions are ignored.
783
}
784                     }
785                 }
786             }
787             // BUBBLING_PHASE: Ancestors are scanned, target to root, for
788
// non-capturing listeners. If the event's preventBubbling flag
789
// has been set before processing of a node commences, we
790
// instead immediately advance to the default phase.
791
// Note that not all events bubble.
792
if (evt.bubbles) {
793                 evt.eventPhase = Event.BUBBLING_PHASE;
794                 int pvsize = pv.size();
795                 for (int j = 0; j < pvsize; j++) {
796                     if (evt.stopPropagation)
797                         break; // Someone set the flag. Phase ends.
798

799                     // Handle all bubbling listeners on this node
800
NodeImpl nn = (NodeImpl) pv.elementAt(j);
801                     evt.currentTarget = nn;
802                     nodeListeners = getEventListeners(nn);
803                     if (nodeListeners != null) {
804                         Vector JavaDoc nl = (Vector JavaDoc) nodeListeners.clone();
805                         // call listeners in the order in which they got
806
// registered
807
int nlsize = nl.size();
808                         for (int i = 0; i < nlsize; i++) {
809                             LEntry le = (LEntry) nl.elementAt(i);
810                             if (!le.useCapture && le.type.equals(evt.type) &&
811                                 nodeListeners.contains(le)) {
812                                 try {
813                                     le.listener.handleEvent(evt);
814                                 }
815                                 catch (Exception JavaDoc e) {
816                                     // All exceptions are ignored.
817
}
818                             }
819                         }
820                     }
821                 }
822             }
823         }
824         
825         // DEFAULT PHASE: Some DOMs have default behaviors bound to specific
826
// nodes. If this DOM does, and if the event's preventDefault flag has
827
// not been set, we now return to the target node and process its
828
// default handler for this event, if any.
829
// No specific phase value defined, since this is DOM-internal
830
if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) {
831             // evt.eventPhase = Event.DEFAULT_PHASE;
832
// evt.currentTarget = node;
833
// DO_DEFAULT_OPERATION
834
}
835
836         return evt.preventDefault;
837     } // dispatchEvent(NodeImpl,Event) :boolean
838

839     /**
840      * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom...
841      * are dispatched to an entire subtree. This is the distribution code
842      * therefor. They DO NOT bubble, thanks be, but may be captured.
843      * <p>
844      * ***** At the moment I'm being sloppy and using the normal
845      * capture dispatcher on every node. This could be optimized hugely
846      * by writing a capture engine that tracks our position in the tree to
847      * update the capture chain without repeated chases up to root.
848      * @param node node to dispatch to
849      * @param n node which was directly inserted or removed
850      * @param e event to be sent to that node and its subtree
851      */

852     protected void dispatchEventToSubtree(NodeImpl node, Node JavaDoc n, Event JavaDoc e) {
853         Vector JavaDoc nodeListeners = getEventListeners(node);
854         if (nodeListeners == null || n == null)
855             return;
856
857         // ***** Recursive implementation. This is excessively expensive,
858
// and should be replaced in conjunction with optimization
859
// mentioned above.
860
((NodeImpl) n).dispatchEvent(e);
861         if (n.getNodeType() == Node.ELEMENT_NODE) {
862             NamedNodeMap JavaDoc a = n.getAttributes();
863             for (int i = a.getLength() - 1; i >= 0; --i)
864                 dispatchEventToSubtree(node, a.item(i), e);
865         }
866         dispatchEventToSubtree(node, n.getFirstChild(), e);
867         dispatchEventToSubtree(node, n.getNextSibling(), e);
868     } // dispatchEventToSubtree(NodeImpl,Node,Event) :void
869

870     /**
871      * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
872      * (two values, the Attr node affected (if any) and its previous
873      * string value. Simple struct, no methods.
874      */

875     class EnclosingAttr implements Serializable JavaDoc
876     {
877         AttrImpl node;
878         String JavaDoc oldvalue;
879     }
880
881     EnclosingAttr savedEnclosingAttr;
882
883     /**
884      * NON-DOM INTERNAL: Convenience wrapper for calling
885      * dispatchAggregateEvents when the context was established
886      * by <code>savedEnclosingAttr</code>.
887      * @param node node to dispatch to
888      * @param ea description of Attr affected by current operation
889      */

890     protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) {
891         if (ea != null)
892             dispatchAggregateEvents(node, ea.node, ea.oldvalue,
893                                     MutationEvent.MODIFICATION);
894         else
895             dispatchAggregateEvents(node, null, null, (short) 0);
896             
897     } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
898

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

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

968     /**
969      * NON-DOM INTERNAL: Pre-mutation context check, in
970      * preparation for later generating DOMAttrModified events.
971      * Determines whether this node is within an Attr
972      * @param node node to get enclosing attribute for
973      * @return either a description of that Attr, or null if none such.
974      */

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

1004    /**
1005     * A method to be called when a character data node has been modified
1006     */

1007    void modifyingCharacterData(NodeImpl node) {
1008        if (mutationEvents) {
1009            saveEnclosingAttr(node);
1010        }
1011    }
1012
1013    /**
1014     * A method to be called when a character data node has been modified
1015     */

1016    void modifiedCharacterData(NodeImpl node, String JavaDoc oldvalue, String JavaDoc value) {
1017        if (mutationEvents) {
1018            // MUTATION POST-EVENTS:
1019
LCount lc =
1020                LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
1021            if (lc.captures + lc.bubbles + lc.defaults > 0) {
1022                MutationEvent JavaDoc me = new MutationEventImpl();
1023                me.initMutationEvent(
1024                                 MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1025                                     true, false, null,
1026                                     oldvalue, value, null, (short) 0);
1027                dispatchEvent(node, me);
1028            }
1029            
1030            // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1031
// if required. (Common to most kinds of mutation)
1032
dispatchAggregateEvents(node, savedEnclosingAttr);
1033        } // End mutation postprocessing
1034
}
1035
1036    /**
1037     * A method to be called when a node is about to be inserted in the tree.
1038     */

1039    void insertingNode(NodeImpl node, boolean replace) {
1040        if (mutationEvents) {
1041            if (!replace) {
1042                saveEnclosingAttr(node);
1043            }
1044        }
1045    }
1046
1047    /**
1048     * A method to be called when a node has been inserted in the tree.
1049     */

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

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

1174    void removedNode(NodeImpl node, boolean replace) {
1175        if (mutationEvents) {
1176            // MUTATION POST-EVENTS:
1177
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1178
// if required. (Common to most kinds of mutation)
1179
if (!replace) {
1180                dispatchAggregateEvents(node, savedEnclosingAttr);
1181            }
1182        } // End mutation postprocessing
1183
}
1184
1185    /**
1186     * A method to be called when a node is about to be replaced in the tree.
1187     */

1188    void replacingNode(NodeImpl node) {
1189        if (mutationEvents) {
1190            saveEnclosingAttr(node);
1191        }
1192    }
1193
1194    /**
1195     * A method to be called when a node has been replaced in the tree.
1196     */

1197    void replacedNode(NodeImpl node) {
1198        if (mutationEvents) {
1199            dispatchAggregateEvents(node, savedEnclosingAttr);
1200        }
1201    }
1202
1203    /**
1204     * A method to be called when an attribute value has been modified
1205     */

1206    void modifiedAttrValue(AttrImpl attr, String JavaDoc oldvalue) {
1207        if (mutationEvents) {
1208            // MUTATION POST-EVENTS:
1209
dispatchAggregateEvents(attr, attr, oldvalue,
1210                                    MutationEvent.MODIFICATION);
1211        }
1212    }
1213
1214    /**
1215     * A method to be called when an attribute node has been set
1216     */

1217    void setAttrNode(AttrImpl attr, AttrImpl previous) {
1218        if (mutationEvents) {
1219            // MUTATION POST-EVENTS:
1220
if (previous == null) {
1221                dispatchAggregateEvents(attr.ownerNode, attr, null,
1222                                        MutationEvent.ADDITION);
1223            }
1224            else {
1225                dispatchAggregateEvents(attr.ownerNode, attr,
1226                                        previous.getNodeValue(),
1227                                        MutationEvent.MODIFICATION);
1228            }
1229        }
1230    }
1231
1232    /**
1233     * A method to be called when an attribute node has been removed
1234     */

1235    void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String JavaDoc name) {
1236        // We can't use the standard dispatchAggregate, since it assumes
1237
// that the Attr is still attached to an owner. This code is
1238
// similar but dispatches to the previous owner, "element".
1239
if (mutationEvents) {
1240            // If we have to send DOMAttrModified (determined earlier),
1241
// do so.
1242
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1243            if (lc.captures + lc.bubbles + lc.defaults > 0) {
1244                MutationEventImpl me= new MutationEventImpl();
1245                me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
1246                                     true, false, attr,
1247                                     attr.getNodeValue(), null, name,
1248                                     MutationEvent.REMOVAL);
1249                dispatchEvent(oldOwner, me);
1250            }
1251
1252            // We can hand off to process DOMSubtreeModified, though.
1253
// Note that only the Element needs to be informed; the
1254
// Attr's subtree has not been changed by this operation.
1255
dispatchAggregateEvents(oldOwner, null, null, (short) 0);
1256        }
1257    }
1258    
1259
1260    /**
1261     * A method to be called when an attribute node has been renamed
1262     */

1263    void renamedAttrNode(Attr JavaDoc oldAt, Attr JavaDoc newAt) {
1264    // REVISIT: To be implemented!!!
1265
}
1266
1267    /**
1268     * A method to be called when an element has been renamed
1269     */

1270    void renamedElement(Element JavaDoc oldEl, Element JavaDoc newEl) {
1271    // REVISIT: To be implemented!!!
1272
}
1273
1274} // class DocumentImpl
1275
Popular Tags