|
|
|
|
|
*/ |
|
/* |
|
* 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class DocumentImpl |
|
extends CoreDocumentImpl |
|
implements DocumentTraversal, DocumentEvent, DocumentRange { |
|
|
|
// |
|
// Constants |
|
// |
|
|
|
|
|
static final long serialVersionUID = 515687835542616694L; |
|
|
|
// |
|
// Data |
|
// |
|
|
|
/** Iterators */ |
|
|
|
protected List<NodeIterator> iterators; |
|
|
|
/** Ranges */ |
|
|
|
protected List<Range> ranges; |
|
|
|
|
|
protected Map<NodeImpl, List<LEntry>> eventListeners; |
|
|
|
|
|
protected boolean mutationEvents = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final ObjectStreamField[] serialPersistentFields = |
|
new ObjectStreamField[] { |
|
new ObjectStreamField("iterators", Vector.class), |
|
new ObjectStreamField("ranges", Vector.class), |
|
new ObjectStreamField("eventListeners", Hashtable.class), |
|
new ObjectStreamField("mutationEvents", boolean.class), |
|
}; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
public DocumentImpl() { |
|
super(); |
|
} |
|
|
|
|
|
public DocumentImpl(boolean grammarAccess) { |
|
super(grammarAccess); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public DocumentImpl(DocumentType doctype) |
|
{ |
|
super(doctype); |
|
} |
|
|
|
|
|
public DocumentImpl(DocumentType doctype, boolean grammarAccess) { |
|
super(doctype, grammarAccess); |
|
} |
|
|
|
// |
|
// Node methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node cloneNode(boolean deep) { |
|
|
|
DocumentImpl newdoc = new DocumentImpl(); |
|
callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED); |
|
cloneNode(newdoc, deep); |
|
|
|
|
|
newdoc.mutationEvents = mutationEvents; |
|
|
|
return newdoc; |
|
|
|
} // cloneNode(boolean):Node |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DOMImplementation getImplementation() { |
|
// Currently implemented as a singleton, since it's hardcoded |
|
|
|
return DOMImplementationImpl.getDOMImplementation(); |
|
} |
|
|
|
// |
|
// DocumentTraversal methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public NodeIterator createNodeIterator(Node root, |
|
short whatToShow, |
|
NodeFilter filter) |
|
{ |
|
return createNodeIterator(root, whatToShow, filter, true); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public NodeIterator createNodeIterator(Node root, |
|
int whatToShow, |
|
NodeFilter filter, |
|
boolean entityReferenceExpansion) |
|
{ |
|
|
|
if (root == null) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null); |
|
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); |
|
} |
|
|
|
NodeIterator iterator = new NodeIteratorImpl(this, |
|
root, |
|
whatToShow, |
|
filter, |
|
entityReferenceExpansion); |
|
if (iterators == null) { |
|
iterators = new ArrayList<>(); |
|
} |
|
|
|
iterators.add(iterator); |
|
|
|
return iterator; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public TreeWalker createTreeWalker(Node root, |
|
short whatToShow, |
|
NodeFilter filter) |
|
{ |
|
return createTreeWalker(root, whatToShow, filter, true); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public TreeWalker createTreeWalker(Node root, |
|
int whatToShow, |
|
NodeFilter filter, |
|
boolean entityReferenceExpansion) |
|
{ |
|
if (root == null) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null); |
|
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); |
|
} |
|
return new TreeWalkerImpl(root, whatToShow, filter, |
|
entityReferenceExpansion); |
|
} |
|
|
|
// |
|
// Not DOM Level 2. Support DocumentTraversal methods. |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void removeRange(Range range) { |
|
|
|
if (range == null) return; |
|
if (ranges == null) return; |
|
|
|
ranges.remove(range); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void replacedText(NodeImpl node) { |
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).receiveReplacedText(node); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void deletedText(NodeImpl node, int offset, int count) { |
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).receiveDeletedText(node, |
|
offset, count); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void insertedText(NodeImpl node, int offset, int count) { |
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).receiveInsertedText(node, |
|
offset, count); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void splitData(Node node, Node newNode, int offset) { |
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).receiveSplitData(node, |
|
newNode, offset); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// DocumentEvent methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void setMutationEvents(boolean set) { |
|
mutationEvents = set; |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean getMutationEvents() { |
|
return mutationEvents; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void setEventListeners(NodeImpl n, List<LEntry> listeners) { |
|
if (eventListeners == null) { |
|
eventListeners = new HashMap<>(); |
|
} |
|
if (listeners == null) { |
|
eventListeners.remove(n); |
|
if (eventListeners.isEmpty()) { |
|
|
|
mutationEvents = false; |
|
} |
|
} else { |
|
eventListeners.put(n, listeners); |
|
|
|
mutationEvents = true; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected List<LEntry> getEventListeners(NodeImpl n) { |
|
if (eventListeners == null) { |
|
return null; |
|
} |
|
return eventListeners.get(n); |
|
} |
|
|
|
// |
|
// EventTarget support (public and internal) |
|
// |
|
|
|
// |
|
// Constants |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class LEntry implements Serializable { |
|
|
|
private static final long serialVersionUID = -8426757059492421631L; |
|
String type; |
|
EventListener listener; |
|
boolean useCapture; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
LEntry(String type, EventListener listener, boolean useCapture) |
|
{ |
|
this.type = type; |
|
this.listener = listener; |
|
this.useCapture = useCapture; |
|
} |
|
|
|
} // LEntry |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@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 |
|
|
|
if (type == null || type.equals("") || listener == null) |
|
return; |
|
|
|
// Each listener may be registered only once per type per phase. |
|
|
|
removeEventListener(node, type, listener, useCapture); |
|
|
|
List<LEntry> nodeListeners = getEventListeners(node); |
|
if(nodeListeners == null) { |
|
nodeListeners = new ArrayList<>(); |
|
setEventListeners(node, nodeListeners); |
|
} |
|
nodeListeners.add(new LEntry(type, listener, useCapture)); |
|
|
|
|
|
LCount lc = LCount.lookup(type); |
|
if (useCapture) { |
|
++lc.captures; |
|
++lc.total; |
|
} |
|
else { |
|
++lc.bubbles; |
|
++lc.total; |
|
} |
|
|
|
} // addEventListener(NodeImpl,String,EventListener,boolean) :void |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
protected void removeEventListener(NodeImpl node, String type, |
|
EventListener listener, |
|
boolean useCapture) |
|
{ |
|
|
|
if (type == null || type.equals("") || listener == null) |
|
return; |
|
List<LEntry> nodeListeners = getEventListeners(node); |
|
if (nodeListeners == null) |
|
return; |
|
|
|
// Note that addListener has previously ensured that |
|
// each listener may be registered only once per type per phase. |
|
|
|
for (int i = nodeListeners.size() - 1; i >= 0; --i) { |
|
LEntry le = nodeListeners.get(i); |
|
if (le.useCapture == useCapture && le.listener == listener && |
|
le.type.equals(type)) { |
|
nodeListeners.remove(i); |
|
|
|
if (nodeListeners.isEmpty()) |
|
setEventListeners(node, null); |
|
|
|
|
|
LCount lc = LCount.lookup(type); |
|
if (useCapture) { |
|
--lc.captures; |
|
--lc.total; |
|
} |
|
else { |
|
--lc.bubbles; |
|
--lc.total; |
|
} |
|
|
|
break; |
|
} |
|
} |
|
} // removeEventListener(NodeImpl,String,EventListener,boolean) :void |
|
|
|
@Override |
|
protected void copyEventListeners(NodeImpl src, NodeImpl tgt) { |
|
List<LEntry> nodeListeners = getEventListeners(src); |
|
if (nodeListeners == null) { |
|
return; |
|
} |
|
setEventListeners(tgt, new ArrayList<>(nodeListeners)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@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 |
|
|
|
EventImpl evt = (EventImpl)event; |
|
|
|
// VALIDATE -- must have been initialized at least once, must have |
|
|
|
if(!evt.initialized || evt.type == null || evt.type.equals("")) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "UNSPECIFIED_EVENT_TYPE_ERR", null); |
|
throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR, msg); |
|
} |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
List<Node> pv = new ArrayList<>(10); |
|
Node p = node; |
|
Node n = p.getParentNode(); |
|
while (n != null) { |
|
pv.add(n); |
|
p = n; |
|
n = n.getParentNode(); |
|
} |
|
|
|
|
|
if (lc.captures > 0) { |
|
evt.eventPhase = Event.CAPTURING_PHASE; |
|
// Ancestors are scanned, root to target, for |
|
|
|
for (int j = pv.size() - 1; j >= 0; --j) { |
|
if (evt.stopPropagation) |
|
break; |
|
|
|
|
|
NodeImpl nn = (NodeImpl) pv.get(j); |
|
evt.currentTarget = nn; |
|
ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(nn); |
|
if (nodeListeners != null) { |
|
List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); |
|
|
|
int nlsize = nl.size(); |
|
for (int i = 0; i < nlsize; i++) { |
|
LEntry le = nl.get(i); |
|
if (le.useCapture && le.type.equals(evt.type) && |
|
nodeListeners.contains(le)) { |
|
try { |
|
le.listener.handleEvent(evt); |
|
} |
|
catch (Exception e) { |
|
// All exceptions are ignored. |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
if (lc.bubbles > 0) { |
|
// AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners |
|
// on the target node. Note that capturing listeners on the target |
|
|
|
evt.eventPhase = Event.AT_TARGET; |
|
evt.currentTarget = node; |
|
ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(node); |
|
if (!evt.stopPropagation && nodeListeners != null) { |
|
List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); |
|
|
|
int nlsize = nl.size(); |
|
for (int i = 0; i < nlsize; i++) { |
|
LEntry le = nl.get(i); |
|
if (!le.useCapture && le.type.equals(evt.type) && |
|
nodeListeners.contains(le)) { |
|
try { |
|
le.listener.handleEvent(evt); |
|
} |
|
catch (Exception e) { |
|
// All exceptions are ignored. |
|
} |
|
} |
|
} |
|
} |
|
// BUBBLING_PHASE: Ancestors are scanned, target to root, for |
|
// non-capturing listeners. If the event's preventBubbling flag |
|
// has been set before processing of a node commences, we |
|
// instead immediately advance to the default phase. |
|
|
|
if (evt.bubbles) { |
|
evt.eventPhase = Event.BUBBLING_PHASE; |
|
int pvsize = pv.size(); |
|
for (int j = 0; j < pvsize; j++) { |
|
if (evt.stopPropagation) |
|
break; |
|
|
|
|
|
NodeImpl nn = (NodeImpl) pv.get(j); |
|
evt.currentTarget = nn; |
|
nodeListeners = (ArrayList<LEntry>)getEventListeners(nn); |
|
if (nodeListeners != null) { |
|
List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); |
|
// call listeners in the order in which they got |
|
|
|
int nlsize = nl.size(); |
|
for (int i = 0; i < nlsize; i++) { |
|
LEntry le = nl.get(i); |
|
if (!le.useCapture && le.type.equals(evt.type) && |
|
nodeListeners.contains(le)) { |
|
try { |
|
le.listener.handleEvent(evt); |
|
} |
|
catch (Exception e) { |
|
// All exceptions are ignored. |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// DEFAULT PHASE: Some DOMs have default behaviors bound to specific |
|
// nodes. If this DOM does, and if the event's preventDefault flag has |
|
// not been set, we now return to the target node and process its |
|
// default handler for this event, if any. |
|
|
|
if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) { |
|
// evt.eventPhase = Event.DEFAULT_PHASE; |
|
// evt.currentTarget = node; |
|
// DO_DEFAULT_OPERATION |
|
} |
|
|
|
return evt.preventDefault; |
|
} // dispatchEvent(NodeImpl,Event) :boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
((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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class EnclosingAttr implements Serializable { |
|
private static final long serialVersionUID = 5208387723391647216L; |
|
AttrImpl node; |
|
String oldvalue; |
|
} |
|
|
|
EnclosingAttr savedEnclosingAttr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void dispatchAggregateEvents(NodeImpl node, |
|
AttrImpl enclosingAttr, |
|
String oldvalue, short change) { |
|
|
|
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 |
|
|
|
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 |
|
|
|
if (enclosingAttr != null) { |
|
dispatchEvent(enclosingAttr, me); |
|
if (owner != null) |
|
dispatchEvent(owner, me); |
|
} |
|
else |
|
dispatchEvent(node, me); |
|
} |
|
} // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
*/ |
|
void modifyingCharacterData(NodeImpl node, boolean replace) { |
|
if (mutationEvents) { |
|
if (!replace) { |
|
saveEnclosingAttr(node); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) { |
|
if (mutationEvents) { |
|
if (!replace) { |
|
|
|
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, |
|
|
|
dispatchAggregateEvents(node, savedEnclosingAttr); |
|
} // End mutation postprocessing |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
modifiedCharacterData(node, oldvalue, value, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void insertingNode(NodeImpl node, boolean replace) { |
|
if (mutationEvents) { |
|
if (!replace) { |
|
saveEnclosingAttr(node); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) { |
|
if (mutationEvents) { |
|
// MUTATION POST-EVENTS: |
|
// "Local" events (non-aggregated) |
|
|
|
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 |
|
|
|
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) { |
|
NodeImpl p = eventAncestor; |
|
while (p != null) { |
|
eventAncestor = p; |
|
// In this context, ancestry includes |
|
|
|
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 |
|
|
|
dispatchAggregateEvents(node, savedEnclosingAttr); |
|
} |
|
} |
|
|
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).insertedNodeFromDOM(newInternal); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) { |
|
|
|
|
|
if (iterators != null) { |
|
int size = iterators.size(); |
|
for (int i = 0; i != size; i++) { |
|
((NodeIteratorImpl)iterators.get(i)).removeNode(oldChild); |
|
} |
|
} |
|
|
|
|
|
if (ranges != null) { |
|
int size = ranges.size(); |
|
for (int i = 0; i != size; i++) { |
|
((RangeImpl)ranges.get(i)).removeNode(oldChild); |
|
} |
|
} |
|
|
|
|
|
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 |
|
|
|
if (!replace) { |
|
saveEnclosingAttr(node); |
|
} |
|
|
|
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 |
|
|
|
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) { |
|
for (NodeImpl p = eventAncestor.parentNode(); |
|
p != null; p = p.parentNode()) { |
|
eventAncestor = p; |
|
} |
|
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 |
|
} |
|
|
|
|
|
|
|
*/ |
|
void removedNode(NodeImpl node, boolean replace) { |
|
if (mutationEvents) { |
|
// MUTATION POST-EVENTS: |
|
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified, |
|
|
|
if (!replace) { |
|
dispatchAggregateEvents(node, savedEnclosingAttr); |
|
} |
|
} // End mutation postprocessing |
|
} |
|
|
|
|
|
|
|
*/ |
|
void replacingNode(NodeImpl node) { |
|
if (mutationEvents) { |
|
saveEnclosingAttr(node); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void replacingData (NodeImpl node) { |
|
if (mutationEvents) { |
|
saveEnclosingAttr(node); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void replacedNode(NodeImpl node) { |
|
if (mutationEvents) { |
|
dispatchAggregateEvents(node, savedEnclosingAttr); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void modifiedAttrValue(AttrImpl attr, String oldvalue) { |
|
if (mutationEvents) { |
|
|
|
dispatchAggregateEvents(attr, attr, oldvalue, |
|
MutationEvent.MODIFICATION); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
void setAttrNode(AttrImpl attr, AttrImpl previous) { |
|
if (mutationEvents) { |
|
|
|
if (previous == null) { |
|
dispatchAggregateEvents(attr.ownerNode, attr, null, |
|
MutationEvent.ADDITION); |
|
} |
|
else { |
|
dispatchAggregateEvents(attr.ownerNode, attr, |
|
previous.getNodeValue(), |
|
MutationEvent.MODIFICATION); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
if (mutationEvents) { |
|
// If we have to send DOMAttrModified (determined earlier), |
|
|
|
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 |
|
|
|
dispatchAggregateEvents(oldOwner, null, null, (short) 0); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void renamedAttrNode(Attr oldAt, Attr newAt) { |
|
// REVISIT: To be implemented!!! |
|
} |
|
|
|
|
|
|
|
*/ |
|
void renamedElement(Element oldEl, Element newEl) { |
|
// REVISIT: To be implemented!!! |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeObject(ObjectOutputStream out) throws IOException { |
|
|
|
Vector<NodeIterator> it = (iterators == null)? null : new Vector<>(iterators); |
|
Vector<Range> r = (ranges == null)? null : new Vector<>(ranges); |
|
|
|
Hashtable<NodeImpl, Vector<LEntry>> el = null; |
|
if (eventListeners != null) { |
|
el = new Hashtable<>(); |
|
for (Map.Entry<NodeImpl, List<LEntry>> e : eventListeners.entrySet()) { |
|
el.put(e.getKey(), new Vector<>(e.getValue())); |
|
} |
|
} |
|
|
|
|
|
ObjectOutputStream.PutField pf = out.putFields(); |
|
pf.put("iterators", it); |
|
pf.put("ranges", r); |
|
pf.put("eventListeners", el); |
|
pf.put("mutationEvents", mutationEvents); |
|
out.writeFields(); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
private void readObject(ObjectInputStream in) |
|
throws IOException, ClassNotFoundException { |
|
|
|
ObjectInputStream.GetField gf = in.readFields(); |
|
Vector<NodeIterator> it = (Vector<NodeIterator>)gf.get("iterators", null); |
|
Vector<Range> r = (Vector<Range>)gf.get("ranges", null); |
|
Hashtable<NodeImpl, Vector<LEntry>> el = |
|
(Hashtable<NodeImpl, Vector<LEntry>>)gf.get("eventListeners", null); |
|
|
|
mutationEvents = gf.get("mutationEvents", false); |
|
|
|
|
|
if (it != null) iterators = new ArrayList<>(it); |
|
if (r != null) ranges = new ArrayList<>(r); |
|
if (el != null) { |
|
eventListeners = new HashMap<>(); |
|
for (Map.Entry<NodeImpl, Vector<LEntry>> e : el.entrySet()) { |
|
eventListeners.put(e.getKey(), new ArrayList<>(e.getValue())); |
|
} |
|
} |
|
} |
|
} // class DocumentImpl |