1 /*
2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xerces.internal.dom;
22 
23 import com.sun.org.apache.xerces.internal.dom.events.EventImpl;
24 import com.sun.org.apache.xerces.internal.dom.events.MutationEventImpl;
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.io.ObjectStreamField;
29 import java.io.Serializable;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.Hashtable;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Vector;
36 import org.w3c.dom.Attr;
37 import org.w3c.dom.DOMException;
38 import org.w3c.dom.DOMImplementation;
39 import org.w3c.dom.DocumentType;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.NamedNodeMap;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.UserDataHandler;
44 import org.w3c.dom.events.DocumentEvent;
45 import org.w3c.dom.events.Event;
46 import org.w3c.dom.events.EventException;
47 import org.w3c.dom.events.EventListener;
48 import org.w3c.dom.events.MutationEvent;
49 import org.w3c.dom.ranges.DocumentRange;
50 import org.w3c.dom.ranges.Range;
51 import org.w3c.dom.traversal.DocumentTraversal;
52 import org.w3c.dom.traversal.NodeFilter;
53 import org.w3c.dom.traversal.NodeIterator;
54 import org.w3c.dom.traversal.TreeWalker;
55 
56 
57 /**
58  * The Document interface represents the entire HTML or XML document.
59  * Conceptually, it is the root of the document tree, and provides the
60  * primary access to the document's data.
61  * <P>
62  * Since elements, text nodes, comments, processing instructions,
63  * etc. cannot exist outside the context of a Document, the Document
64  * interface also contains the factory methods needed to create these
65  * objects. The Node objects created have a ownerDocument attribute
66  * which associates them with the Document within whose context they
67  * were created.
68  * <p>
69  * The DocumentImpl class also implements the DOM Level 2 DocumentTraversal
70  * interface. This interface is comprised of factory methods needed to
71  * create NodeIterators and TreeWalkers. The process of creating NodeIterator
72  * objects also adds these references to this document.
73  * After finishing with an iterator it is important to remove the object
74  * using the remove methods in this implementation. This allows the release of
75  * the references from the iterator objects to the DOM Nodes.
76  * <p>
77  * <b>Note:</b> When any node in the document is serialized, the
78  * entire document is serialized along with it.
79  *
80  * @xerces.internal
81  *
82  * @author Arnaud  Le Hors, IBM
83  * @author Joe Kesselman, IBM
84  * @author Andy Clark, IBM
85  * @author Ralf Pfeiffer, IBM
86  * @since  PR-DOM-Level-1-19980818.
87  * @LastModified: Nov 2017
88  */
89 public class DocumentImpl
90     extends CoreDocumentImpl
91     implements DocumentTraversal, DocumentEvent, DocumentRange {
92 
93     //
94     // Constants
95     //
96 
97     /** Serialization version. */
98     static final long serialVersionUID = 515687835542616694L;
99 
100     //
101     // Data
102     //
103 
104     /** Iterators */
105     // REVISIT: Should this be transient? -Ac
106     protected List<NodeIterator> iterators;
107 
108      /** Ranges */
109     // REVISIT: Should this be transient? -Ac
110     protected List<Range> ranges;
111 
112     /** Table for event listeners registered to this document nodes. */
113     protected Map<NodeImpl, List<LEntry>> eventListeners;
114 
115     /** Bypass mutation events firing. */
116     protected boolean mutationEvents = false;
117 
118 
119     /**
120      * @serialField iterators Vector Node iterators
121      * @serialField ranges Vector ranges
122      * @serialField eventListeners Hashtable Event listeners
123      * @serialField mutationEvents boolean Bypass mutation events firing
124      */
125     private static final ObjectStreamField[] serialPersistentFields =
126         new ObjectStreamField[] {
127             new ObjectStreamField("iterators", Vector.class),
128             new ObjectStreamField("ranges", Vector.class),
129             new ObjectStreamField("eventListeners", Hashtable.class),
130             new ObjectStreamField("mutationEvents", boolean.class),
131         };
132 
133     //
134     // Constructors
135     //
136 
137     /**
138      * NON-DOM: Actually creating a Document is outside the DOM's spec,
139      * since it has to operate in terms of a particular implementation.
140      */
DocumentImpl()141     public DocumentImpl() {
142         super();
143     }
144 
145     /** Constructor. */
DocumentImpl(boolean grammarAccess)146     public DocumentImpl(boolean grammarAccess) {
147         super(grammarAccess);
148     }
149 
150     /**
151      * For DOM2 support.
152      * The createDocument factory method is in DOMImplementation.
153      */
DocumentImpl(DocumentType doctype)154     public DocumentImpl(DocumentType doctype)
155     {
156         super(doctype);
157     }
158 
159     /** For DOM2 support. */
DocumentImpl(DocumentType doctype, boolean grammarAccess)160     public DocumentImpl(DocumentType doctype, boolean grammarAccess) {
161         super(doctype, grammarAccess);
162     }
163 
164     //
165     // Node methods
166     //
167 
168     /**
169      * Deep-clone a document, including fixing ownerDoc for the cloned
170      * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR
171      * protection. I've chosen to implement it by calling importNode
172      * which is DOM Level 2.
173      *
174      * @return org.w3c.dom.Node
175      * @param deep boolean, iff true replicate children
176      */
cloneNode(boolean deep)177     public Node cloneNode(boolean deep) {
178 
179         DocumentImpl newdoc = new DocumentImpl();
180         callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED);
181         cloneNode(newdoc, deep);
182 
183         // experimental
184         newdoc.mutationEvents = mutationEvents;
185 
186         return newdoc;
187 
188     } // cloneNode(boolean):Node
189 
190     /**
191      * Retrieve information describing the abilities of this particular
192      * DOM implementation. Intended to support applications that may be
193      * using DOMs retrieved from several different sources, potentially
194      * with different underlying representations.
195      */
getImplementation()196     public DOMImplementation getImplementation() {
197         // Currently implemented as a singleton, since it's hardcoded
198         // information anyway.
199         return DOMImplementationImpl.getDOMImplementation();
200     }
201 
202     //
203     // DocumentTraversal methods
204     //
205 
206     /**
207      * NON-DOM extension:
208      * Create and return a NodeIterator. The NodeIterator is
209      * added to a list of NodeIterators so that it can be
210      * removed to free up the DOM Nodes it references.
211      *
212      * @param root The root of the iterator.
213      * @param whatToShow The whatToShow mask.
214      * @param filter The NodeFilter installed. Null means no filter.
215      */
createNodeIterator(Node root, short whatToShow, NodeFilter filter)216     public NodeIterator createNodeIterator(Node root,
217                                            short whatToShow,
218                                            NodeFilter filter)
219     {
220         return createNodeIterator(root, whatToShow, filter, true);
221     }
222 
223     /**
224      * Create and return a NodeIterator. The NodeIterator is
225      * added to a list of NodeIterators so that it can be
226      * removed to free up the DOM Nodes it references.
227      *
228      * @param root The root of the iterator.
229      * @param whatToShow The whatToShow mask.
230      * @param filter The NodeFilter installed. Null means no filter.
231      * @param entityReferenceExpansion true to expand the contents of
232      *                                 EntityReference nodes
233      * @since WD-DOM-Level-2-19990923
234      */
createNodeIterator(Node root, int whatToShow, NodeFilter filter, boolean entityReferenceExpansion)235     public NodeIterator createNodeIterator(Node root,
236                                            int whatToShow,
237                                            NodeFilter filter,
238                                            boolean entityReferenceExpansion)
239     {
240 
241         if (root == null) {
242                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
243                   throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
244         }
245 
246         NodeIterator iterator = new NodeIteratorImpl(this,
247                                                      root,
248                                                      whatToShow,
249                                                      filter,
250                                                      entityReferenceExpansion);
251         if (iterators == null) {
252             iterators = new ArrayList<>();
253         }
254 
255         iterators.add(iterator);
256 
257         return iterator;
258     }
259 
260     /**
261      * NON-DOM extension:
262      * Create and return a TreeWalker.
263      *
264      * @param root The root of the iterator.
265      * @param whatToShow The whatToShow mask.
266      * @param filter The NodeFilter installed. Null means no filter.
267      */
createTreeWalker(Node root, short whatToShow, NodeFilter filter)268     public TreeWalker createTreeWalker(Node root,
269                                        short whatToShow,
270                                        NodeFilter filter)
271     {
272         return createTreeWalker(root, whatToShow, filter, true);
273     }
274     /**
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      * @param entityReferenceExpansion true to expand the contents of
281      *                                 EntityReference nodes
282      * @since WD-DOM-Level-2-19990923
283      */
createTreeWalker(Node root, int whatToShow, NodeFilter filter, boolean entityReferenceExpansion)284     public TreeWalker createTreeWalker(Node root,
285                                        int whatToShow,
286                                        NodeFilter filter,
287                                        boolean entityReferenceExpansion)
288     {
289         if (root == null) {
290             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
291             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
292         }
293         return new TreeWalkerImpl(root, whatToShow, filter,
294                                   entityReferenceExpansion);
295     }
296 
297     //
298     // Not DOM Level 2. Support DocumentTraversal methods.
299     //
300 
301     /** This is not called by the developer client. The
302      *  developer client uses the detach() function on the
303      *  NodeIterator itself. <p>
304      *
305      *  This function is called from the NodeIterator#detach().
306      */
removeNodeIterator(NodeIterator nodeIterator)307      void removeNodeIterator(NodeIterator nodeIterator) {
308 
309         if (nodeIterator == null) return;
310         if (iterators == null) return;
311 
312         iterators.remove(nodeIterator);
313     }
314 
315     //
316     // DocumentRange methods
317     //
318     /**
319      */
createRange()320     public Range createRange() {
321 
322         if (ranges == null) {
323             ranges = new ArrayList<>();
324         }
325 
326         Range range = new RangeImpl(this);
327         ranges.add(range);
328 
329         return range;
330 
331     }
332 
333     /** Not a client function. Called by Range.detach(),
334      *  so a Range can remove itself from the list of
335      *  Ranges.
336      */
removeRange(Range range)337     void removeRange(Range range) {
338 
339         if (range == null) return;
340         if (ranges == null) return;
341 
342         ranges.remove(range);
343     }
344 
345     /**
346      * A method to be called when some text was changed in a text node,
347      * so that live objects can be notified.
348      */
replacedText(NodeImpl node)349     void replacedText(NodeImpl node) {
350         // notify ranges
351         if (ranges != null) {
352             int size = ranges.size();
353             for (int i = 0; i != size; i++) {
354                 ((RangeImpl)ranges.get(i)).receiveReplacedText(node);
355             }
356         }
357     }
358 
359     /**
360      * A method to be called when some text was deleted from a text node,
361      * so that live objects can be notified.
362      */
deletedText(NodeImpl node, int offset, int count)363     void deletedText(NodeImpl node, int offset, int count) {
364         // notify ranges
365         if (ranges != null) {
366             int size = ranges.size();
367             for (int i = 0; i != size; i++) {
368                 ((RangeImpl)ranges.get(i)).receiveDeletedText(node,
369                                                                 offset, count);
370             }
371         }
372     }
373 
374     /**
375      * A method to be called when some text was inserted into a text node,
376      * so that live objects can be notified.
377      */
insertedText(NodeImpl node, int offset, int count)378     void insertedText(NodeImpl node, int offset, int count) {
379         // notify ranges
380         if (ranges != null) {
381             int size = ranges.size();
382             for (int i = 0; i != size; i++) {
383                 ((RangeImpl)ranges.get(i)).receiveInsertedText(node,
384                                                                 offset, count);
385             }
386         }
387     }
388 
389     /**
390      * A method to be called when a text node has been split,
391      * so that live objects can be notified.
392      */
splitData(Node node, Node newNode, int offset)393     void splitData(Node node, Node newNode, int offset) {
394         // notify ranges
395         if (ranges != null) {
396             int size = ranges.size();
397             for (int i = 0; i != size; i++) {
398                 ((RangeImpl)ranges.get(i)).receiveSplitData(node,
399                                                               newNode, offset);
400             }
401         }
402     }
403 
404     //
405     // DocumentEvent methods
406     //
407 
408     /**
409      * Introduced in DOM Level 2. Optional. <p>
410      * Create and return Event objects.
411      *
412      * @param type The eventType parameter specifies the type of Event
413      * interface to be created.  If the Event interface specified is supported
414      * by the implementation this method will return a new Event of the
415      * interface type requested. If the Event is to be dispatched via the
416      * dispatchEvent method the appropriate event init method must be called
417      * after creation in order to initialize the Event's values.  As an
418      * example, a user wishing to synthesize some kind of Event would call
419      * createEvent with the parameter "Events". The initEvent method could then
420      * be called on the newly created Event to set the specific type of Event
421      * to be dispatched and set its context information.
422      * @return Newly created Event
423      * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
424      * does not support the type of Event interface requested
425      * @since WD-DOM-Level-2-19990923
426      */
createEvent(String type)427     public Event createEvent(String type)
428         throws DOMException {
429             if (type.equalsIgnoreCase("Events") || "Event".equals(type))
430                 return new EventImpl();
431             if (type.equalsIgnoreCase("MutationEvents") ||
432                 "MutationEvent".equals(type))
433                 return new MutationEventImpl();
434             else {
435             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
436                 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
437         }
438         }
439 
440     /**
441      * Sets whether the DOM implementation generates mutation events
442      * upon operations.
443      */
setMutationEvents(boolean set)444     void setMutationEvents(boolean set) {
445         mutationEvents = set;
446     }
447 
448     /**
449      * Returns true if the DOM implementation generates mutation events.
450      */
getMutationEvents()451     boolean getMutationEvents() {
452         return mutationEvents;
453     }
454 
455     /**
456      * Store event listener registered on a given node
457      * This is another place where we could use weak references! Indeed, the
458      * node here won't be GC'ed as long as some listener is registered on it,
459      * since the eventsListeners table will have a reference to the node.
460      */
setEventListeners(NodeImpl n, List<LEntry> listeners)461     protected void setEventListeners(NodeImpl n, List<LEntry> listeners) {
462         if (eventListeners == null) {
463             eventListeners = new HashMap<>();
464         }
465         if (listeners == null) {
466             eventListeners.remove(n);
467             if (eventListeners.isEmpty()) {
468                 // stop firing events when there isn't any listener
469                 mutationEvents = false;
470             }
471         } else {
472             eventListeners.put(n, listeners);
473             // turn mutation events on
474             mutationEvents = true;
475         }
476     }
477 
478     /**
479      * Retreive event listener registered on a given node
480      */
getEventListeners(NodeImpl n)481     protected List<LEntry> getEventListeners(NodeImpl n) {
482         if (eventListeners == null) {
483             return null;
484         }
485         return eventListeners.get(n);
486     }
487 
488     //
489     // EventTarget support (public and internal)
490     //
491 
492     //
493     // Constants
494     //
495 
496     /*
497      * NON-DOM INTERNAL: Class LEntry is just a struct used to represent
498      * event listeners registered with this node. Copies of this object
499      * are hung from the nodeListeners Vector.
500      * <p>
501      * I considered using two vectors -- one for capture,
502      * one for bubble -- but decided that since the list of listeners
503      * is probably short in most cases, it might not be worth spending
504      * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
505      */
506     class LEntry implements Serializable {
507 
508         private static final long serialVersionUID = -8426757059492421631L;
509         String type;
510         EventListener listener;
511         boolean useCapture;
512 
513         /** NON-DOM INTERNAL: Constructor for Listener list Entry
514          * @param type Event name (NOT event group!) to listen for.
515          * @param listener Who gets called when event is dispatched
516          * @param useCaptue True iff listener is registered on
517          *  capturing phase rather than at-target or bubbling
518          */
LEntry(String type, EventListener listener, boolean useCapture)519         LEntry(String type, EventListener listener, boolean useCapture)
520         {
521             this.type = type;
522             this.listener = listener;
523             this.useCapture = useCapture;
524         }
525 
526     } // LEntry
527 
528     /**
529      * Introduced in DOM Level 2. <p> Register an event listener with this
530      * Node. A listener may be independently registered as both Capturing and
531      * Bubbling, but may only be registered once per role; redundant
532      * registrations are ignored.
533      * @param node node to add listener to
534      * @param type Event name (NOT event group!) to listen for.
535      * @param listener Who gets called when event is dispatched
536      * @param useCapture True iff listener is registered on
537      *  capturing phase rather than at-target or bubbling
538      */
539     @Override
addEventListener(NodeImpl node, String type, EventListener listener, boolean useCapture)540     protected void addEventListener(NodeImpl node, String type,
541                                     EventListener listener, boolean useCapture)
542     {
543         // We can't dispatch to blank type-name, and of course we need
544         // a listener to dispatch to
545         if (type == null || type.equals("") || listener == null)
546             return;
547 
548         // Each listener may be registered only once per type per phase.
549         // Simplest way to code that is to zap the previous entry, if any.
550         removeEventListener(node, type, listener, useCapture);
551 
552         List<LEntry> nodeListeners = getEventListeners(node);
553         if(nodeListeners == null) {
554             nodeListeners = new ArrayList<>();
555             setEventListeners(node, nodeListeners);
556         }
557         nodeListeners.add(new LEntry(type, listener, useCapture));
558 
559         // Record active listener
560         LCount lc = LCount.lookup(type);
561         if (useCapture) {
562             ++lc.captures;
563             ++lc.total;
564         }
565         else {
566             ++lc.bubbles;
567             ++lc.total;
568         }
569 
570     } // addEventListener(NodeImpl,String,EventListener,boolean) :void
571 
572     /**
573      * Introduced in DOM Level 2. <p> Deregister an event listener previously
574      * registered with this Node.  A listener must be independently removed
575      * from the Capturing and Bubbling roles. Redundant removals (of listeners
576      * not currently registered for this role) are ignored.
577      * @param node node to remove listener from
578      * @param type Event name (NOT event group!) to listen for.
579      * @param listener Who gets called when event is dispatched
580      * @param useCapture True iff listener is registered on
581      *  capturing phase rather than at-target or bubbling
582      */
583     @Override
removeEventListener(NodeImpl node, String type, EventListener listener, boolean useCapture)584     protected void removeEventListener(NodeImpl node, String type,
585                                        EventListener listener,
586                                        boolean useCapture)
587     {
588         // If this couldn't be a valid listener registration, ignore request
589         if (type == null || type.equals("") || listener == null)
590             return;
591         List<LEntry> nodeListeners = getEventListeners(node);
592         if (nodeListeners == null)
593             return;
594 
595         // Note that addListener has previously ensured that
596         // each listener may be registered only once per type per phase.
597         // count-down is OK for deletions!
598         for (int i = nodeListeners.size() - 1; i >= 0; --i) {
599             LEntry le = nodeListeners.get(i);
600             if (le.useCapture == useCapture && le.listener == listener &&
601                 le.type.equals(type)) {
602                 nodeListeners.remove(i);
603                 // Storage management: Discard empty listener lists
604                 if (nodeListeners.isEmpty())
605                     setEventListeners(node, null);
606 
607                 // Remove active listener
608                 LCount lc = LCount.lookup(type);
609                 if (useCapture) {
610                     --lc.captures;
611                     --lc.total;
612                 }
613                 else {
614                     --lc.bubbles;
615                     --lc.total;
616                 }
617 
618                 break;  // Found it; no need to loop farther.
619             }
620         }
621     } // removeEventListener(NodeImpl,String,EventListener,boolean) :void
622 
623     @Override
copyEventListeners(NodeImpl src, NodeImpl tgt)624     protected void copyEventListeners(NodeImpl src, NodeImpl tgt) {
625         List<LEntry> nodeListeners = getEventListeners(src);
626         if (nodeListeners == null) {
627             return;
628         }
629         setEventListeners(tgt, new ArrayList<>(nodeListeners));
630     }
631 
632     /**
633      * Introduced in DOM Level 2. <p>
634      * Distribution engine for DOM Level 2 Events.
635      * <p>
636      * Event propagation runs as follows:
637      * <ol>
638      * <li>Event is dispatched to a particular target node, which invokes
639      *   this code. Note that the event's stopPropagation flag is
640      *   cleared when dispatch begins; thereafter, if it has
641      *   been set before processing of a node commences, we instead
642      *   immediately advance to the DEFAULT phase.
643      * <li>The node's ancestors are established as destinations for events.
644      *   For capture and bubble purposes, node ancestry is determined at
645      *   the time dispatch starts. If an event handler alters the document
646      *   tree, that does not change which nodes will be informed of the event.
647      * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for
648      *   Capturing listeners. If found, they are invoked (see below).
649      * <li>AT_TARGET:
650      *   Event is dispatched to NON-CAPTURING listeners on the
651      *   target node. Note that capturing listeners on this node are _not_
652      *   invoked.
653      * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for
654      *   non-capturing listeners.
655      * <li>Default processing: Some DOMs have default behaviors bound to
656      *   specific nodes. If this DOM does, and if the event's preventDefault
657      *   flag has not been set, we now return to the target node and process
658      *   its default handler for this event, if any.
659      * </ol>
660      * <p>
661      * Note that registration of handlers during processing of an event does
662      * not take effect during this phase of this event; they will not be called
663      * until the next time this node is visited by dispatchEvent. On the other
664      * hand, removals take effect immediately.
665      * <p>
666      * If an event handler itself causes events to be dispatched, they are
667      * processed synchronously, before processing resumes
668      * on the event which triggered them. Please be aware that this may
669      * result in events arriving at listeners "out of order" relative
670      * to the actual sequence of requests.
671      * <p>
672      * Note that our implementation resets the event's stop/prevent flags
673      * when dispatch begins.
674      * I believe the DOM's intent is that event objects be redispatchable,
675      * though it isn't stated in those terms.
676      * @param node node to dispatch to
677      * @param event the event object to be dispatched to
678      *              registered EventListeners
679      * @return true if the event's <code>preventDefault()</code>
680      *              method was invoked by an EventListener; otherwise false.
681     */
682     @Override
683     @SuppressWarnings({"rawtypes", "unchecked"})
dispatchEvent(NodeImpl node, Event event)684     protected boolean dispatchEvent(NodeImpl node, Event 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 msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "UNSPECIFIED_EVENT_TYPE_ERR", null);
695             throw new EventException(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.total == 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         List<Node> pv = new ArrayList<>(10);
721         Node p = node;
722         Node n = p.getParentNode();
723         while (n != null) {
724             pv.add(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.get(j);
740                 evt.currentTarget = nn;
741                 ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(nn);
742                 if (nodeListeners != null) {
743                     List<LEntry> nl = (ArrayList<LEntry>)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 = nl.get(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 e) {
754                                 // All exceptions are ignored.
755                             }
756                         }
757                     }
758                 }
759             }
760         }
761 
762 
763         // Both AT_TARGET and BUBBLE use non-capturing listeners.
764         if (lc.bubbles > 0) {
765             // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners
766             // on the target node. Note that capturing listeners on the target
767             // node are _not_ invoked, even during the capture phase.
768             evt.eventPhase = Event.AT_TARGET;
769             evt.currentTarget = node;
770             ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(node);
771             if (!evt.stopPropagation && nodeListeners != null) {
772                 List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone();
773                 // call listeners in the order in which they got registered
774                 int nlsize = nl.size();
775                 for (int i = 0; i < nlsize; i++) {
776                     LEntry le = nl.get(i);
777                     if (!le.useCapture && le.type.equals(evt.type) &&
778                         nodeListeners.contains(le)) {
779                         try {
780                             le.listener.handleEvent(evt);
781                         }
782                         catch (Exception e) {
783                             // All exceptions are ignored.
784                         }
785                     }
786                 }
787             }
788             // BUBBLING_PHASE: Ancestors are scanned, target to root, for
789             // non-capturing listeners. If the event's preventBubbling flag
790             // has been set before processing of a node commences, we
791             // instead immediately advance to the default phase.
792             // Note that not all events bubble.
793             if (evt.bubbles) {
794                 evt.eventPhase = Event.BUBBLING_PHASE;
795                 int pvsize = pv.size();
796                 for (int j = 0; j < pvsize; j++) {
797                     if (evt.stopPropagation)
798                         break;  // Someone set the flag. Phase ends.
799 
800                     // Handle all bubbling listeners on this node
801                     NodeImpl nn = (NodeImpl) pv.get(j);
802                     evt.currentTarget = nn;
803                     nodeListeners = (ArrayList<LEntry>)getEventListeners(nn);
804                     if (nodeListeners != null) {
805                         List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone();
806                         // call listeners in the order in which they got
807                         // registered
808                         int nlsize = nl.size();
809                         for (int i = 0; i < nlsize; i++) {
810                             LEntry le = nl.get(i);
811                             if (!le.useCapture && le.type.equals(evt.type) &&
812                                 nodeListeners.contains(le)) {
813                                 try {
814                                     le.listener.handleEvent(evt);
815                                 }
816                                 catch (Exception e) {
817                                     // All exceptions are ignored.
818                                 }
819                             }
820                         }
821                     }
822                 }
823             }
824         }
825 
826         // DEFAULT PHASE: Some DOMs have default behaviors bound to specific
827         // nodes. If this DOM does, and if the event's preventDefault flag has
828         // not been set, we now return to the target node and process its
829         // default handler for this event, if any.
830         // No specific phase value defined, since this is DOM-internal
831         if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) {
832             // evt.eventPhase = Event.DEFAULT_PHASE;
833             // evt.currentTarget = node;
834             // DO_DEFAULT_OPERATION
835         }
836 
837         return evt.preventDefault;
838     } // dispatchEvent(NodeImpl,Event) :boolean
839 
840     /**
841      * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom...
842      * are dispatched to an entire subtree. This is the distribution code
843      * therefor. They DO NOT bubble, thanks be, but may be captured.
844      * <p>
845      * Similar to code in dispatchingEventToSubtree however this method
846      * is only used on the target node and does not start a dispatching chain
847      * on the sibling of the target node as this is not part of the subtree
848      * ***** At the moment I'm being sloppy and using the normal
849      * capture dispatcher on every node. This could be optimized hugely
850      * by writing a capture engine that tracks our position in the tree to
851      * update the capture chain without repeated chases up to root.
852      * @param n target node (that was directly inserted or removed)
853      * @param e event to be sent to that node and its subtree
854      */
dispatchEventToSubtree(Node n, Event e)855     protected void dispatchEventToSubtree(Node n, Event e) {
856 
857         ((NodeImpl) n).dispatchEvent(e);
858         if (n.getNodeType() == Node.ELEMENT_NODE) {
859             NamedNodeMap a = n.getAttributes();
860             for (int i = a.getLength() - 1; i >= 0; --i)
861                 dispatchingEventToSubtree(a.item(i), e);
862         }
863         dispatchingEventToSubtree(n.getFirstChild(), e);
864 
865     } // dispatchEventToSubtree(NodeImpl,Node,Event) :void
866 
867 
868     /**
869      * Dispatches event to the target node's descendents recursively
870      *
871      * @param n node to dispatch to
872      * @param e event to be sent to that node and its subtree
873      */
dispatchingEventToSubtree(Node n, Event e)874     protected void dispatchingEventToSubtree(Node n, Event e) {
875         if (n==null)
876                 return;
877 
878         // ***** Recursive implementation. This is excessively expensive,
879         // and should be replaced in conjunction with optimization
880         // mentioned above.
881         ((NodeImpl) n).dispatchEvent(e);
882         if (n.getNodeType() == Node.ELEMENT_NODE) {
883             NamedNodeMap a = n.getAttributes();
884             for (int i = a.getLength() - 1; i >= 0; --i)
885                 dispatchingEventToSubtree(a.item(i), e);
886         }
887         dispatchingEventToSubtree(n.getFirstChild(), e);
888         dispatchingEventToSubtree(n.getNextSibling(), e);
889     }
890 
891     /**
892      * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
893      * (two values, the Attr node affected (if any) and its previous
894      * string value. Simple struct, no methods.
895      */
896     class EnclosingAttr implements Serializable {
897         private static final long serialVersionUID = 5208387723391647216L;
898         AttrImpl node;
899         String oldvalue;
900     }
901 
902     EnclosingAttr savedEnclosingAttr;
903 
904     /**
905      * NON-DOM INTERNAL: Convenience wrapper for calling
906      * dispatchAggregateEvents when the context was established
907      * by <code>savedEnclosingAttr</code>.
908      * @param node node to dispatch to
909      * @param ea description of Attr affected by current operation
910      */
dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea)911     protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) {
912         if (ea != null)
913             dispatchAggregateEvents(node, ea.node, ea.oldvalue,
914                                     MutationEvent.MODIFICATION);
915         else
916             dispatchAggregateEvents(node, null, null, (short) 0);
917 
918     } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
919 
920     /**
921      * NON-DOM INTERNAL: Generate the "aggregated" post-mutation events
922      * DOMAttrModified and DOMSubtreeModified.
923      * Both of these should be issued only once for each user-requested
924      * mutation operation, even if that involves multiple changes to
925      * the DOM.
926      * For example, if a DOM operation makes multiple changes to a single
927      * Attr before returning, it would be nice to generate only one
928      * DOMAttrModified, and multiple changes over larger scope but within
929      * a recognizable single subtree might want to generate only one
930      * DOMSubtreeModified, sent to their lowest common ancestor.
931      * <p>
932      * To manage this, use the "internal" versions of insert and remove
933      * with MUTATION_LOCAL, then make an explicit call to this routine
934      * at the higher level. Some examples now exist in our code.
935      *
936      * @param node The node to dispatch to
937      * @param enclosingAttr The Attr node (if any) whose value has been changed
938      * as a result of the DOM operation. Null if none such.
939      * @param oldValue The String value previously held by the
940      * enclosingAttr. Ignored if none such.
941      * @param change Type of modification to the attr. See
942      * MutationEvent.attrChange
943      */
dispatchAggregateEvents(NodeImpl node, AttrImpl enclosingAttr, String oldvalue, short change)944     protected void dispatchAggregateEvents(NodeImpl node,
945                                            AttrImpl enclosingAttr,
946                                            String oldvalue, short change) {
947         // We have to send DOMAttrModified.
948         NodeImpl owner = null;
949         if (enclosingAttr != null) {
950             LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
951             owner = (NodeImpl) enclosingAttr.getOwnerElement();
952             if (lc.total > 0) {
953                 if (owner != null) {
954                     MutationEventImpl me =  new MutationEventImpl();
955                     me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
956                                          true, false, enclosingAttr,
957                                          oldvalue,
958                                          enclosingAttr.getNodeValue(),
959                                          enclosingAttr.getNodeName(),
960                                          change);
961                     owner.dispatchEvent(me);
962                 }
963             }
964         }
965         // DOMSubtreeModified gets sent to the lowest common root of a
966         // set of changes.
967         // "This event is dispatched after all other events caused by the
968         // mutation have been fired."
969         LCount lc = LCount.lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED);
970         if (lc.total > 0) {
971             MutationEvent me =  new MutationEventImpl();
972             me.initMutationEvent(MutationEventImpl.DOM_SUBTREE_MODIFIED,
973                                  true, false, null, null,
974                                  null, null, (short) 0);
975 
976             // If we're within an Attr, DStM gets sent to the Attr
977             // and to its owningElement. Otherwise we dispatch it
978             // locally.
979             if (enclosingAttr != null) {
980                 dispatchEvent(enclosingAttr, me);
981                 if (owner != null)
982                     dispatchEvent(owner, me);
983             }
984             else
985                 dispatchEvent(node, me);
986         }
987     } // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void
988 
989     /**
990      * NON-DOM INTERNAL: Pre-mutation context check, in
991      * preparation for later generating DOMAttrModified events.
992      * Determines whether this node is within an Attr
993      * @param node node to get enclosing attribute for
994      * @return either a description of that Attr, or null if none such.
995      */
saveEnclosingAttr(NodeImpl node)996     protected void saveEnclosingAttr(NodeImpl node) {
997         savedEnclosingAttr = null;
998         // MUTATION PREPROCESSING AND PRE-EVENTS:
999         // If we're within the scope of an Attr and DOMAttrModified
1000         // was requested, we need to preserve its previous value for
1001         // that event.
1002         LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1003         if (lc.total > 0) {
1004             NodeImpl eventAncestor = node;
1005             while (true) {
1006                 if (eventAncestor == null)
1007                     return;
1008                 int type = eventAncestor.getNodeType();
1009                 if (type == Node.ATTRIBUTE_NODE) {
1010                     EnclosingAttr retval = new EnclosingAttr();
1011                     retval.node = (AttrImpl) eventAncestor;
1012                     retval.oldvalue = retval.node.getNodeValue();
1013                     savedEnclosingAttr = retval;
1014                     return;
1015                 }
1016                 else if (type == Node.ENTITY_REFERENCE_NODE)
1017                     eventAncestor = eventAncestor.parentNode();
1018                 else if (type == Node.TEXT_NODE)
1019                     eventAncestor = eventAncestor.parentNode();
1020                 else
1021                     return;
1022                 // Any other parent means we're not in an Attr
1023             }
1024         }
1025     } // saveEnclosingAttr(NodeImpl) :void
1026 
1027     /**
1028      * A method to be called when a character data node has been modified
1029      */
modifyingCharacterData(NodeImpl node, boolean replace)1030     void modifyingCharacterData(NodeImpl node, boolean replace) {
1031         if (mutationEvents) {
1032                 if (!replace) {
1033                         saveEnclosingAttr(node);
1034                 }
1035         }
1036     }
1037 
1038     /**
1039      * A method to be called when a character data node has been modified
1040      */
modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace)1041     void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
1042         if (mutationEvents) {
1043                 if (!replace) {
1044                         // MUTATION POST-EVENTS:
1045                         LCount lc =
1046                                 LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
1047                         if (lc.total > 0) {
1048                                 MutationEvent me = new MutationEventImpl();
1049                                 me.initMutationEvent(
1050                                         MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1051                                         true, false, null,
1052                                                                                 oldvalue, value, null, (short) 0);
1053                                 dispatchEvent(node, me);
1054                         }
1055 
1056                         // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1057                         // if required. (Common to most kinds of mutation)
1058                         dispatchAggregateEvents(node, savedEnclosingAttr);
1059                 } // End mutation postprocessing
1060         }
1061     }
1062 
1063     /**
1064      * A method to be called when a character data node has been replaced
1065      */
replacedCharacterData(NodeImpl node, String oldvalue, String value)1066     void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
1067         //now that we have finished replacing data, we need to perform the same actions
1068         //that are required after a character data node has been modified
1069         //send the value of false for replace parameter so that mutation
1070         //events if appropriate will be initiated
1071         modifiedCharacterData(node, oldvalue, value, false);
1072     }
1073 
1074 
1075 
1076     /**
1077      * A method to be called when a node is about to be inserted in the tree.
1078      */
insertingNode(NodeImpl node, boolean replace)1079     void insertingNode(NodeImpl node, boolean replace) {
1080         if (mutationEvents) {
1081             if (!replace) {
1082                 saveEnclosingAttr(node);
1083             }
1084         }
1085     }
1086 
1087     /**
1088      * A method to be called when a node has been inserted in the tree.
1089      */
insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace)1090     void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
1091         if (mutationEvents) {
1092             // MUTATION POST-EVENTS:
1093             // "Local" events (non-aggregated)
1094             // New child is told it was inserted, and where
1095             LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
1096             if (lc.total > 0) {
1097                 MutationEventImpl me = new MutationEventImpl();
1098                 me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED,
1099                                      true, false, node,
1100                                      null, null, null, (short) 0);
1101                 dispatchEvent(newInternal, me);
1102             }
1103 
1104             // If within the Document, tell the subtree it's been added
1105             // to the Doc.
1106             lc = LCount.lookup(
1107                             MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
1108             if (lc.total > 0) {
1109                 NodeImpl eventAncestor = node;
1110                 if (savedEnclosingAttr != null)
1111                     eventAncestor = (NodeImpl)
1112                         savedEnclosingAttr.node.getOwnerElement();
1113                 if (eventAncestor != null) { // Might have been orphan Attr
1114                     NodeImpl p = eventAncestor;
1115                     while (p != null) {
1116                         eventAncestor = p; // Last non-null ancestor
1117                         // In this context, ancestry includes
1118                         // walking back from Attr to Element
1119                         if (p.getNodeType() == ATTRIBUTE_NODE) {
1120                             p = (NodeImpl) ((AttrImpl)p).getOwnerElement();
1121                         }
1122                         else {
1123                             p = p.parentNode();
1124                         }
1125                     }
1126                     if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
1127                         MutationEventImpl me = new MutationEventImpl();
1128                         me.initMutationEvent(MutationEventImpl
1129                                              .DOM_NODE_INSERTED_INTO_DOCUMENT,
1130                                              false,false,null,null,
1131                                              null,null,(short)0);
1132                         dispatchEventToSubtree(newInternal, me);
1133                     }
1134                 }
1135             }
1136             if (!replace) {
1137                 // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
1138                 // (Common to most kinds of mutation)
1139                 dispatchAggregateEvents(node, savedEnclosingAttr);
1140             }
1141         }
1142 
1143         // notify the range of insertions
1144         if (ranges != null) {
1145             int size = ranges.size();
1146             for (int i = 0; i != size; i++) {
1147                 ((RangeImpl)ranges.get(i)).insertedNodeFromDOM(newInternal);
1148             }
1149         }
1150     }
1151 
1152     /**
1153      * A method to be called when a node is about to be removed from the tree.
1154      */
removingNode(NodeImpl node, NodeImpl oldChild, boolean replace)1155     void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
1156 
1157         // notify iterators
1158         if (iterators != null) {
1159             int size = iterators.size();
1160             for (int i = 0; i != size; i++) {
1161                ((NodeIteratorImpl)iterators.get(i)).removeNode(oldChild);
1162             }
1163         }
1164 
1165         // notify ranges
1166         if (ranges != null) {
1167             int size = ranges.size();
1168             for (int i = 0; i != size; i++) {
1169                 ((RangeImpl)ranges.get(i)).removeNode(oldChild);
1170             }
1171         }
1172 
1173         // mutation events
1174         if (mutationEvents) {
1175             // MUTATION PREPROCESSING AND PRE-EVENTS:
1176             // If we're within the scope of an Attr and DOMAttrModified
1177             // was requested, we need to preserve its previous value for
1178             // that event.
1179             if (!replace) {
1180                 saveEnclosingAttr(node);
1181             }
1182             // Child is told that it is about to be removed
1183             LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
1184             if (lc.total > 0) {
1185                 MutationEventImpl me= new MutationEventImpl();
1186                 me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
1187                                      true, false, node, null,
1188                                      null, null, (short) 0);
1189                 dispatchEvent(oldChild, me);
1190             }
1191 
1192             // If within Document, child's subtree is informed that it's
1193             // losing that status
1194             lc = LCount.lookup(
1195                              MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
1196             if (lc.total > 0) {
1197                 NodeImpl eventAncestor = this;
1198                 if(savedEnclosingAttr != null)
1199                     eventAncestor = (NodeImpl)
1200                         savedEnclosingAttr.node.getOwnerElement();
1201                 if (eventAncestor != null) { // Might have been orphan Attr
1202                     for (NodeImpl p = eventAncestor.parentNode();
1203                          p != null; p = p.parentNode()) {
1204                         eventAncestor = p; // Last non-null ancestor
1205                     }
1206                     if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
1207                         MutationEventImpl me = new MutationEventImpl();
1208                         me.initMutationEvent(
1209                               MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
1210                                              false, false, null,
1211                                              null, null, null, (short) 0);
1212                         dispatchEventToSubtree(oldChild, me);
1213                     }
1214                 }
1215             }
1216         } // End mutation preprocessing
1217     }
1218 
1219     /**
1220      * A method to be called when a node has been removed from the tree.
1221      */
removedNode(NodeImpl node, boolean replace)1222     void removedNode(NodeImpl node, boolean replace) {
1223         if (mutationEvents) {
1224             // MUTATION POST-EVENTS:
1225             // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1226             // if required. (Common to most kinds of mutation)
1227             if (!replace) {
1228                 dispatchAggregateEvents(node, savedEnclosingAttr);
1229             }
1230         } // End mutation postprocessing
1231     }
1232 
1233     /**
1234      * A method to be called when a node is about to be replaced in the tree.
1235      */
replacingNode(NodeImpl node)1236     void replacingNode(NodeImpl node) {
1237         if (mutationEvents) {
1238             saveEnclosingAttr(node);
1239         }
1240     }
1241 
1242     /**
1243      * A method to be called when character data is about to be replaced in the tree.
1244      */
replacingData(NodeImpl node)1245     void replacingData (NodeImpl node) {
1246         if (mutationEvents) {
1247                         saveEnclosingAttr(node);
1248         }
1249     }
1250 
1251     /**
1252      * A method to be called when a node has been replaced in the tree.
1253      */
replacedNode(NodeImpl node)1254     void replacedNode(NodeImpl node) {
1255         if (mutationEvents) {
1256             dispatchAggregateEvents(node, savedEnclosingAttr);
1257         }
1258     }
1259 
1260     /**
1261      * A method to be called when an attribute value has been modified
1262      */
modifiedAttrValue(AttrImpl attr, String oldvalue)1263     void modifiedAttrValue(AttrImpl attr, String oldvalue) {
1264         if (mutationEvents) {
1265             // MUTATION POST-EVENTS:
1266             dispatchAggregateEvents(attr, attr, oldvalue,
1267                                     MutationEvent.MODIFICATION);
1268         }
1269     }
1270 
1271     /**
1272      * A method to be called when an attribute node has been set
1273      */
setAttrNode(AttrImpl attr, AttrImpl previous)1274     void setAttrNode(AttrImpl attr, AttrImpl previous) {
1275         if (mutationEvents) {
1276             // MUTATION POST-EVENTS:
1277             if (previous == null) {
1278                 dispatchAggregateEvents(attr.ownerNode, attr, null,
1279                                         MutationEvent.ADDITION);
1280             }
1281             else {
1282                 dispatchAggregateEvents(attr.ownerNode, attr,
1283                                         previous.getNodeValue(),
1284                                         MutationEvent.MODIFICATION);
1285             }
1286         }
1287     }
1288 
1289     /**
1290      * A method to be called when an attribute node has been removed
1291      */
removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name)1292     void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
1293         // We can't use the standard dispatchAggregate, since it assumes
1294         // that the Attr is still attached to an owner. This code is
1295         // similar but dispatches to the previous owner, "element".
1296         if (mutationEvents) {
1297             // If we have to send DOMAttrModified (determined earlier),
1298             // do so.
1299             LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1300             if (lc.total > 0) {
1301                 MutationEventImpl me= new MutationEventImpl();
1302                 me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
1303                                      true, false, attr,
1304                                      attr.getNodeValue(), null, name,
1305                                      MutationEvent.REMOVAL);
1306                 dispatchEvent(oldOwner, me);
1307             }
1308 
1309             // We can hand off to process DOMSubtreeModified, though.
1310             // Note that only the Element needs to be informed; the
1311             // Attr's subtree has not been changed by this operation.
1312             dispatchAggregateEvents(oldOwner, null, null, (short) 0);
1313         }
1314     }
1315 
1316 
1317     /**
1318      * A method to be called when an attribute node has been renamed
1319      */
renamedAttrNode(Attr oldAt, Attr newAt)1320     void renamedAttrNode(Attr oldAt, Attr newAt) {
1321         // REVISIT: To be implemented!!!
1322     }
1323 
1324     /**
1325      * A method to be called when an element has been renamed
1326      */
renamedElement(Element oldEl, Element newEl)1327     void renamedElement(Element oldEl, Element newEl) {
1328         // REVISIT: To be implemented!!!
1329     }
1330 
1331 
1332     /**
1333      * @serialData Serialized fields. Convert Maps to Hashtables and Lists
1334      * to Vectors for backward compatibility.
1335      */
writeObject(ObjectOutputStream out)1336     private void writeObject(ObjectOutputStream out) throws IOException {
1337         // Convert Maps to Hashtables, Lists to Vectors
1338         Vector<NodeIterator> it = (iterators == null)? null : new Vector<>(iterators);
1339         Vector<Range> r = (ranges == null)? null : new Vector<>(ranges);
1340 
1341         Hashtable<NodeImpl, Vector<LEntry>> el = null;
1342         if (eventListeners != null) {
1343             el = new Hashtable<>();
1344             for (Map.Entry<NodeImpl, List<LEntry>> e : eventListeners.entrySet()) {
1345                  el.put(e.getKey(), new Vector<>(e.getValue()));
1346             }
1347         }
1348 
1349         // Write serialized fields
1350         ObjectOutputStream.PutField pf = out.putFields();
1351         pf.put("iterators", it);
1352         pf.put("ranges", r);
1353         pf.put("eventListeners", el);
1354         pf.put("mutationEvents", mutationEvents);
1355         out.writeFields();
1356     }
1357 
1358     @SuppressWarnings("unchecked")
readObject(ObjectInputStream in)1359     private void readObject(ObjectInputStream in)
1360                         throws IOException, ClassNotFoundException {
1361         // We have to read serialized fields first.
1362         ObjectInputStream.GetField gf = in.readFields();
1363         Vector<NodeIterator> it = (Vector<NodeIterator>)gf.get("iterators", null);
1364         Vector<Range> r = (Vector<Range>)gf.get("ranges", null);
1365         Hashtable<NodeImpl, Vector<LEntry>> el =
1366                 (Hashtable<NodeImpl, Vector<LEntry>>)gf.get("eventListeners", null);
1367 
1368         mutationEvents = gf.get("mutationEvents", false);
1369 
1370         //convert Hashtables back to HashMaps and Vectors to Lists
1371         if (it != null) iterators = new ArrayList<>(it);
1372         if (r != null) ranges = new ArrayList<>(r);
1373         if (el != null) {
1374             eventListeners = new HashMap<>();
1375             for (Map.Entry<NodeImpl, Vector<LEntry>> e : el.entrySet()) {
1376                  eventListeners.put(e.getKey(), new ArrayList<>(e.getValue()));
1377             }
1378         }
1379     }
1380 } // class DocumentImpl
1381