/* * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xerces.internal.dom; import com.sun.org.apache.xerces.internal.dom.events.EventImpl; import com.sun.org.apache.xerces.internal.dom.events.MutationEventImpl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Vector; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.UserDataHandler; import org.w3c.dom.events.DocumentEvent; import org.w3c.dom.events.Event; import org.w3c.dom.events.EventException; import org.w3c.dom.events.EventListener; import org.w3c.dom.events.MutationEvent; import org.w3c.dom.ranges.DocumentRange; import org.w3c.dom.ranges.Range; import org.w3c.dom.traversal.DocumentTraversal; import org.w3c.dom.traversal.NodeFilter; import org.w3c.dom.traversal.NodeIterator; import org.w3c.dom.traversal.TreeWalker; /** * The Document interface represents the entire HTML or XML document. * Conceptually, it is the root of the document tree, and provides the * primary access to the document's data. *
* Since elements, text nodes, comments, processing instructions, * etc. cannot exist outside the context of a Document, the Document * interface also contains the factory methods needed to create these * objects. The Node objects created have a ownerDocument attribute * which associates them with the Document within whose context they * were created. *
* The DocumentImpl class also implements the DOM Level 2 DocumentTraversal * interface. This interface is comprised of factory methods needed to * create NodeIterators and TreeWalkers. The process of creating NodeIterator * objects also adds these references to this document. * After finishing with an iterator it is important to remove the object * using the remove methods in this implementation. This allows the release of * the references from the iterator objects to the DOM Nodes. *
* Note: When any node in the document is serialized, the
* entire document is serialized along with it.
*
* @xerces.internal
*
* @author Arnaud Le Hors, IBM
* @author Joe Kesselman, IBM
* @author Andy Clark, IBM
* @author Ralf Pfeiffer, IBM
* @since PR-DOM-Level-1-19980818.
* @LastModified: Nov 2017
*/
public class DocumentImpl
extends CoreDocumentImpl
implements DocumentTraversal, DocumentEvent, DocumentRange {
//
// Constants
//
/** Serialization version. */
static final long serialVersionUID = 515687835542616694L;
//
// Data
//
/** Iterators */
// REVISIT: Should this be transient? -Ac
protected List
*
* This function is called from the NodeIterator#detach().
*/
void removeNodeIterator(NodeIterator nodeIterator) {
if (nodeIterator == null) return;
if (iterators == null) return;
iterators.remove(nodeIterator);
}
//
// DocumentRange methods
//
/**
*/
public Range createRange() {
if (ranges == null) {
ranges = new ArrayList<>();
}
Range range = new RangeImpl(this);
ranges.add(range);
return range;
}
/** Not a client function. Called by Range.detach(),
* so a Range can remove itself from the list of
* Ranges.
*/
void removeRange(Range range) {
if (range == null) return;
if (ranges == null) return;
ranges.remove(range);
}
/**
* A method to be called when some text was changed in a text node,
* so that live objects can be notified.
*/
void replacedText(NodeImpl node) {
// notify ranges
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).receiveReplacedText(node);
}
}
}
/**
* A method to be called when some text was deleted from a text node,
* so that live objects can be notified.
*/
void deletedText(NodeImpl node, int offset, int count) {
// notify ranges
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).receiveDeletedText(node,
offset, count);
}
}
}
/**
* A method to be called when some text was inserted into a text node,
* so that live objects can be notified.
*/
void insertedText(NodeImpl node, int offset, int count) {
// notify ranges
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).receiveInsertedText(node,
offset, count);
}
}
}
/**
* A method to be called when a text node has been split,
* so that live objects can be notified.
*/
void splitData(Node node, Node newNode, int offset) {
// notify ranges
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).receiveSplitData(node,
newNode, offset);
}
}
}
//
// DocumentEvent methods
//
/**
* Introduced in DOM Level 2. Optional.
* Create and return Event objects.
*
* @param type The eventType parameter specifies the type of Event
* interface to be created. If the Event interface specified is supported
* by the implementation this method will return a new Event of the
* interface type requested. If the Event is to be dispatched via the
* dispatchEvent method the appropriate event init method must be called
* after creation in order to initialize the Event's values. As an
* example, a user wishing to synthesize some kind of Event would call
* createEvent with the parameter "Events". The initEvent method could then
* be called on the newly created Event to set the specific type of Event
* to be dispatched and set its context information.
* @return Newly created Event
* @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation
* does not support the type of Event interface requested
* @since WD-DOM-Level-2-19990923
*/
public Event createEvent(String type)
throws DOMException {
if (type.equalsIgnoreCase("Events") || "Event".equals(type))
return new EventImpl();
if (type.equalsIgnoreCase("MutationEvents") ||
"MutationEvent".equals(type))
return new MutationEventImpl();
else {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null);
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
}
}
/**
* Sets whether the DOM implementation generates mutation events
* upon operations.
*/
void setMutationEvents(boolean set) {
mutationEvents = set;
}
/**
* Returns true if the DOM implementation generates mutation events.
*/
boolean getMutationEvents() {
return mutationEvents;
}
/**
* Store event listener registered on a given node
* This is another place where we could use weak references! Indeed, the
* node here won't be GC'ed as long as some listener is registered on it,
* since the eventsListeners table will have a reference to the node.
*/
protected void setEventListeners(NodeImpl n, List
* I considered using two vectors -- one for capture,
* one for bubble -- but decided that since the list of listeners
* is probably short in most cases, it might not be worth spending
* the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE.
*/
class LEntry implements Serializable {
private static final long serialVersionUID = -8426757059492421631L;
String type;
EventListener listener;
boolean useCapture;
/** NON-DOM INTERNAL: Constructor for Listener list Entry
* @param type Event name (NOT event group!) to listen for.
* @param listener Who gets called when event is dispatched
* @param useCaptue True iff listener is registered on
* capturing phase rather than at-target or bubbling
*/
LEntry(String type, EventListener listener, boolean useCapture)
{
this.type = type;
this.listener = listener;
this.useCapture = useCapture;
}
} // LEntry
/**
* Introduced in DOM Level 2. Register an event listener with this
* Node. A listener may be independently registered as both Capturing and
* Bubbling, but may only be registered once per role; redundant
* registrations are ignored.
* @param node node to add listener to
* @param type Event name (NOT event group!) to listen for.
* @param listener Who gets called when event is dispatched
* @param useCapture True iff listener is registered on
* capturing phase rather than at-target or bubbling
*/
@Override
protected void addEventListener(NodeImpl node, String type,
EventListener listener, boolean useCapture)
{
// We can't dispatch to blank type-name, and of course we need
// a listener to dispatch to
if (type == null || type.isEmpty() || listener == null)
return;
// Each listener may be registered only once per type per phase.
// Simplest way to code that is to zap the previous entry, if any.
removeEventListener(node, type, listener, useCapture);
List Deregister an event listener previously
* registered with this Node. A listener must be independently removed
* from the Capturing and Bubbling roles. Redundant removals (of listeners
* not currently registered for this role) are ignored.
* @param node node to remove listener from
* @param type Event name (NOT event group!) to listen for.
* @param listener Who gets called when event is dispatched
* @param useCapture True iff listener is registered on
* capturing phase rather than at-target or bubbling
*/
@Override
protected void removeEventListener(NodeImpl node, String type,
EventListener listener,
boolean useCapture)
{
// If this couldn't be a valid listener registration, ignore request
if (type == null || type.isEmpty() || listener == null)
return;
List
* Distribution engine for DOM Level 2 Events.
*
* Event propagation runs as follows:
*
* Note that registration of handlers during processing of an event does
* not take effect during this phase of this event; they will not be called
* until the next time this node is visited by dispatchEvent. On the other
* hand, removals take effect immediately.
*
* If an event handler itself causes events to be dispatched, they are
* processed synchronously, before processing resumes
* on the event which triggered them. Please be aware that this may
* result in events arriving at listeners "out of order" relative
* to the actual sequence of requests.
*
* Note that our implementation resets the event's stop/prevent flags
* when dispatch begins.
* I believe the DOM's intent is that event objects be redispatchable,
* though it isn't stated in those terms.
* @param node node to dispatch to
* @param event the event object to be dispatched to
* registered EventListeners
* @return true if the event's
* Similar to code in dispatchingEventToSubtree however this method
* is only used on the target node and does not start a dispatching chain
* on the sibling of the target node as this is not part of the subtree
* ***** At the moment I'm being sloppy and using the normal
* capture dispatcher on every node. This could be optimized hugely
* by writing a capture engine that tracks our position in the tree to
* update the capture chain without repeated chases up to root.
* @param n target node (that was directly inserted or removed)
* @param e event to be sent to that node and its subtree
*/
protected void dispatchEventToSubtree(Node n, Event e) {
((NodeImpl) n).dispatchEvent(e);
if (n.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap a = n.getAttributes();
for (int i = a.getLength() - 1; i >= 0; --i)
dispatchingEventToSubtree(a.item(i), e);
}
dispatchingEventToSubtree(n.getFirstChild(), e);
} // dispatchEventToSubtree(NodeImpl,Node,Event) :void
/**
* Dispatches event to the target node's descendents recursively
*
* @param n node to dispatch to
* @param e event to be sent to that node and its subtree
*/
protected void dispatchingEventToSubtree(Node n, Event e) {
if (n==null)
return;
// ***** Recursive implementation. This is excessively expensive,
// and should be replaced in conjunction with optimization
// mentioned above.
((NodeImpl) n).dispatchEvent(e);
if (n.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap a = n.getAttributes();
for (int i = a.getLength() - 1; i >= 0; --i)
dispatchingEventToSubtree(a.item(i), e);
}
dispatchingEventToSubtree(n.getFirstChild(), e);
dispatchingEventToSubtree(n.getNextSibling(), e);
}
/**
* NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries
* (two values, the Attr node affected (if any) and its previous
* string value. Simple struct, no methods.
*/
class EnclosingAttr implements Serializable {
private static final long serialVersionUID = 5208387723391647216L;
AttrImpl node;
String oldvalue;
}
EnclosingAttr savedEnclosingAttr;
/**
* NON-DOM INTERNAL: Convenience wrapper for calling
* dispatchAggregateEvents when the context was established
* by
* To manage this, use the "internal" versions of insert and remove
* with MUTATION_LOCAL, then make an explicit call to this routine
* at the higher level. Some examples now exist in our code.
*
* @param node The node to dispatch to
* @param enclosingAttr The Attr node (if any) whose value has been changed
* as a result of the DOM operation. Null if none such.
* @param oldValue The String value previously held by the
* enclosingAttr. Ignored if none such.
* @param change Type of modification to the attr. See
* MutationEvent.attrChange
*/
protected void dispatchAggregateEvents(NodeImpl node,
AttrImpl enclosingAttr,
String oldvalue, short change) {
// We have to send DOMAttrModified.
NodeImpl owner = null;
if (enclosingAttr != null) {
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
owner = (NodeImpl) enclosingAttr.getOwnerElement();
if (lc.total > 0) {
if (owner != null) {
MutationEventImpl me = new MutationEventImpl();
me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
true, false, enclosingAttr,
oldvalue,
enclosingAttr.getNodeValue(),
enclosingAttr.getNodeName(),
change);
owner.dispatchEvent(me);
}
}
}
// DOMSubtreeModified gets sent to the lowest common root of a
// set of changes.
// "This event is dispatched after all other events caused by the
// mutation have been fired."
LCount lc = LCount.lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED);
if (lc.total > 0) {
MutationEvent me = new MutationEventImpl();
me.initMutationEvent(MutationEventImpl.DOM_SUBTREE_MODIFIED,
true, false, null, null,
null, null, (short) 0);
// If we're within an Attr, DStM gets sent to the Attr
// and to its owningElement. Otherwise we dispatch it
// locally.
if (enclosingAttr != null) {
dispatchEvent(enclosingAttr, me);
if (owner != null)
dispatchEvent(owner, me);
}
else
dispatchEvent(node, me);
}
} // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void
/**
* NON-DOM INTERNAL: Pre-mutation context check, in
* preparation for later generating DOMAttrModified events.
* Determines whether this node is within an Attr
* @param node node to get enclosing attribute for
* @return either a description of that Attr, or null if none such.
*/
protected void saveEnclosingAttr(NodeImpl node) {
savedEnclosingAttr = null;
// MUTATION PREPROCESSING AND PRE-EVENTS:
// If we're within the scope of an Attr and DOMAttrModified
// was requested, we need to preserve its previous value for
// that event.
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
if (lc.total > 0) {
NodeImpl eventAncestor = node;
while (true) {
if (eventAncestor == null)
return;
int type = eventAncestor.getNodeType();
if (type == Node.ATTRIBUTE_NODE) {
EnclosingAttr retval = new EnclosingAttr();
retval.node = (AttrImpl) eventAncestor;
retval.oldvalue = retval.node.getNodeValue();
savedEnclosingAttr = retval;
return;
}
else if (type == Node.ENTITY_REFERENCE_NODE)
eventAncestor = eventAncestor.parentNode();
else if (type == Node.TEXT_NODE)
eventAncestor = eventAncestor.parentNode();
else
return;
// Any other parent means we're not in an Attr
}
}
} // saveEnclosingAttr(NodeImpl) :void
/**
* A method to be called when a character data node has been modified
*/
void modifyingCharacterData(NodeImpl node, boolean replace) {
if (mutationEvents) {
if (!replace) {
saveEnclosingAttr(node);
}
}
}
/**
* A method to be called when a character data node has been modified
*/
void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) {
if (mutationEvents) {
if (!replace) {
// MUTATION POST-EVENTS:
LCount lc =
LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
if (lc.total > 0) {
MutationEvent me = new MutationEventImpl();
me.initMutationEvent(
MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
true, false, null,
oldvalue, value, null, (short) 0);
dispatchEvent(node, me);
}
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
// if required. (Common to most kinds of mutation)
dispatchAggregateEvents(node, savedEnclosingAttr);
} // End mutation postprocessing
}
}
/**
* A method to be called when a character data node has been replaced
*/
void replacedCharacterData(NodeImpl node, String oldvalue, String value) {
//now that we have finished replacing data, we need to perform the same actions
//that are required after a character data node has been modified
//send the value of false for replace parameter so that mutation
//events if appropriate will be initiated
modifiedCharacterData(node, oldvalue, value, false);
}
/**
* A method to be called when a node is about to be inserted in the tree.
*/
void insertingNode(NodeImpl node, boolean replace) {
if (mutationEvents) {
if (!replace) {
saveEnclosingAttr(node);
}
}
}
/**
* A method to be called when a node has been inserted in the tree.
*/
void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) {
if (mutationEvents) {
// MUTATION POST-EVENTS:
// "Local" events (non-aggregated)
// New child is told it was inserted, and where
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
if (lc.total > 0) {
MutationEventImpl me = new MutationEventImpl();
me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED,
true, false, node,
null, null, null, (short) 0);
dispatchEvent(newInternal, me);
}
// If within the Document, tell the subtree it's been added
// to the Doc.
lc = LCount.lookup(
MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
if (lc.total > 0) {
NodeImpl eventAncestor = node;
if (savedEnclosingAttr != null)
eventAncestor = (NodeImpl)
savedEnclosingAttr.node.getOwnerElement();
if (eventAncestor != null) { // Might have been orphan Attr
NodeImpl p = eventAncestor;
while (p != null) {
eventAncestor = p; // Last non-null ancestor
// In this context, ancestry includes
// walking back from Attr to Element
if (p.getNodeType() == ATTRIBUTE_NODE) {
p = (NodeImpl) ((AttrImpl)p).getOwnerElement();
}
else {
p = p.parentNode();
}
}
if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
MutationEventImpl me = new MutationEventImpl();
me.initMutationEvent(MutationEventImpl
.DOM_NODE_INSERTED_INTO_DOCUMENT,
false,false,null,null,
null,null,(short)0);
dispatchEventToSubtree(newInternal, me);
}
}
}
if (!replace) {
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
// (Common to most kinds of mutation)
dispatchAggregateEvents(node, savedEnclosingAttr);
}
}
// notify the range of insertions
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).insertedNodeFromDOM(newInternal);
}
}
}
/**
* A method to be called when a node is about to be removed from the tree.
*/
void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) {
// notify iterators
if (iterators != null) {
int size = iterators.size();
for (int i = 0; i != size; i++) {
((NodeIteratorImpl)iterators.get(i)).removeNode(oldChild);
}
}
// notify ranges
if (ranges != null) {
int size = ranges.size();
for (int i = 0; i != size; i++) {
((RangeImpl)ranges.get(i)).removeNode(oldChild);
}
}
// mutation events
if (mutationEvents) {
// MUTATION PREPROCESSING AND PRE-EVENTS:
// If we're within the scope of an Attr and DOMAttrModified
// was requested, we need to preserve its previous value for
// that event.
if (!replace) {
saveEnclosingAttr(node);
}
// Child is told that it is about to be removed
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
if (lc.total > 0) {
MutationEventImpl me= new MutationEventImpl();
me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
true, false, node, null,
null, null, (short) 0);
dispatchEvent(oldChild, me);
}
// If within Document, child's subtree is informed that it's
// losing that status
lc = LCount.lookup(
MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
if (lc.total > 0) {
NodeImpl eventAncestor = this;
if(savedEnclosingAttr != null)
eventAncestor = (NodeImpl)
savedEnclosingAttr.node.getOwnerElement();
if (eventAncestor != null) { // Might have been orphan Attr
for (NodeImpl p = eventAncestor.parentNode();
p != null; p = p.parentNode()) {
eventAncestor = p; // Last non-null ancestor
}
if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){
MutationEventImpl me = new MutationEventImpl();
me.initMutationEvent(
MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
false, false, null,
null, null, null, (short) 0);
dispatchEventToSubtree(oldChild, me);
}
}
}
} // End mutation preprocessing
}
/**
* A method to be called when a node has been removed from the tree.
*/
void removedNode(NodeImpl node, boolean replace) {
if (mutationEvents) {
// MUTATION POST-EVENTS:
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
// if required. (Common to most kinds of mutation)
if (!replace) {
dispatchAggregateEvents(node, savedEnclosingAttr);
}
} // End mutation postprocessing
}
/**
* A method to be called when a node is about to be replaced in the tree.
*/
void replacingNode(NodeImpl node) {
if (mutationEvents) {
saveEnclosingAttr(node);
}
}
/**
* A method to be called when character data is about to be replaced in the tree.
*/
void replacingData (NodeImpl node) {
if (mutationEvents) {
saveEnclosingAttr(node);
}
}
/**
* A method to be called when a node has been replaced in the tree.
*/
void replacedNode(NodeImpl node) {
if (mutationEvents) {
dispatchAggregateEvents(node, savedEnclosingAttr);
}
}
/**
* A method to be called when an attribute value has been modified
*/
void modifiedAttrValue(AttrImpl attr, String oldvalue) {
if (mutationEvents) {
// MUTATION POST-EVENTS:
dispatchAggregateEvents(attr, attr, oldvalue,
MutationEvent.MODIFICATION);
}
}
/**
* A method to be called when an attribute node has been set
*/
void setAttrNode(AttrImpl attr, AttrImpl previous) {
if (mutationEvents) {
// MUTATION POST-EVENTS:
if (previous == null) {
dispatchAggregateEvents(attr.ownerNode, attr, null,
MutationEvent.ADDITION);
}
else {
dispatchAggregateEvents(attr.ownerNode, attr,
previous.getNodeValue(),
MutationEvent.MODIFICATION);
}
}
}
/**
* A method to be called when an attribute node has been removed
*/
void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) {
// We can't use the standard dispatchAggregate, since it assumes
// that the Attr is still attached to an owner. This code is
// similar but dispatches to the previous owner, "element".
if (mutationEvents) {
// If we have to send DOMAttrModified (determined earlier),
// do so.
LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
if (lc.total > 0) {
MutationEventImpl me= new MutationEventImpl();
me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED,
true, false, attr,
attr.getNodeValue(), null, name,
MutationEvent.REMOVAL);
dispatchEvent(oldOwner, me);
}
// We can hand off to process DOMSubtreeModified, though.
// Note that only the Element needs to be informed; the
// Attr's subtree has not been changed by this operation.
dispatchAggregateEvents(oldOwner, null, null, (short) 0);
}
}
/**
* A method to be called when an attribute node has been renamed
*/
void renamedAttrNode(Attr oldAt, Attr newAt) {
// REVISIT: To be implemented!!!
}
/**
* A method to be called when an element has been renamed
*/
void renamedElement(Element oldEl, Element newEl) {
// REVISIT: To be implemented!!!
}
/**
* @serialData Serialized fields. Convert Maps to Hashtables and Lists
* to Vectors for backward compatibility.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// Convert Maps to Hashtables, Lists to Vectors
Vector
*
* preventDefault()
* method was invoked by an EventListener; otherwise false.
*/
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
protected boolean dispatchEvent(NodeImpl node, Event event) {
if (event == null) return false;
// Can't use anyone else's implementation, since there's no public
// API for setting the event's processing-state fields.
EventImpl evt = (EventImpl)event;
// VALIDATE -- must have been initialized at least once, must have
// a non-null non-blank name.
if(!evt.initialized || evt.type == null || evt.type.isEmpty()) {
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "UNSPECIFIED_EVENT_TYPE_ERR", null);
throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR, msg);
}
// If nobody is listening for this event, discard immediately
LCount lc = LCount.lookup(evt.getType());
if (lc.total == 0)
return evt.preventDefault;
// INITIALIZE THE EVENT'S DISPATCH STATUS
// (Note that Event objects are reusable in our implementation;
// that doesn't seem to be explicitly guaranteed in the DOM, but
// I believe it is the intent.)
evt.target = node;
evt.stopPropagation = false;
evt.preventDefault = false;
// Capture pre-event parentage chain, not including target;
// use pre-event-dispatch ancestors even if event handlers mutate
// document and change the target's context.
// Note that this is parents ONLY; events do not
// cross the Attr/Element "blood/brain barrier".
// DOMAttrModified. which looks like an exception,
// is issued to the Element rather than the Attr
// and causes a _second_ DOMSubtreeModified in the Element's
// tree.
ListsavedEnclosingAttr
.
* @param node node to dispatch to
* @param ea description of Attr affected by current operation
*/
protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) {
if (ea != null)
dispatchAggregateEvents(node, ea.node, ea.oldvalue,
MutationEvent.MODIFICATION);
else
dispatchAggregateEvents(node, null, null, (short) 0);
} // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void
/**
* NON-DOM INTERNAL: Generate the "aggregated" post-mutation events
* DOMAttrModified and DOMSubtreeModified.
* Both of these should be issued only once for each user-requested
* mutation operation, even if that involves multiple changes to
* the DOM.
* For example, if a DOM operation makes multiple changes to a single
* Attr before returning, it would be nice to generate only one
* DOMAttrModified, and multiple changes over larger scope but within
* a recognizable single subtree might want to generate only one
* DOMSubtreeModified, sent to their lowest common ancestor.
*