|
|
|
|
|
*/ |
|
/* |
|
* 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 java.io.IOException; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectOutputStream; |
|
|
|
import org.w3c.dom.TypeInfo; |
|
import org.w3c.dom.Attr; |
|
import org.w3c.dom.DOMException; |
|
import org.w3c.dom.Element; |
|
import org.w3c.dom.Node; |
|
import org.w3c.dom.NodeList; |
|
import org.w3c.dom.Text; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class AttrImpl |
|
extends NodeImpl |
|
implements Attr, TypeInfo{ |
|
|
|
// |
|
// Constants |
|
// |
|
|
|
|
|
static final long serialVersionUID = 7277707688218972102L; |
|
|
|
|
|
static final String DTD_URI = "http://www.w3.org/TR/REC-xml"; |
|
|
|
// |
|
// Data |
|
// |
|
|
|
|
|
protected Object value = null; |
|
|
|
|
|
protected String name; |
|
|
|
/** Type information */ |
|
|
|
transient Object type; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
protected AttrImpl(CoreDocumentImpl ownerDocument, String name) { |
|
super(ownerDocument); |
|
this.name = name; |
|
|
|
isSpecified(true); |
|
hasStringValue(true); |
|
} |
|
|
|
|
|
protected AttrImpl() {} |
|
|
|
// Support for DOM Level 3 renameNode method. |
|
// Note: This only deals with part of the pb. It is expected to be |
|
// called after the Attr has been detached for one thing. |
|
|
|
void rename(String name) { |
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
this.name = name; |
|
} |
|
|
|
|
|
protected void makeChildNode() { |
|
if (hasStringValue()) { |
|
if (value != null) { |
|
TextImpl text = |
|
(TextImpl) ownerDocument().createTextNode((String) value); |
|
value = text; |
|
text.isFirstChild(true); |
|
text.previousSibling = text; |
|
text.ownerNode = this; |
|
text.isOwned(true); |
|
} |
|
hasStringValue(false); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected void setOwnerDocument(CoreDocumentImpl doc) { |
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
super.setOwnerDocument(doc); |
|
if (!hasStringValue()) { |
|
for (ChildNode child = (ChildNode) value; |
|
child != null; child = child.nextSibling) { |
|
child.setOwnerDocument(doc); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setIdAttribute(boolean id){ |
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
isIdAttribute(id); |
|
} |
|
|
|
public boolean isId(){ |
|
// REVISIT: should an attribute that is not in the tree return |
|
|
|
return isIdAttribute(); |
|
} |
|
|
|
|
|
// |
|
// Node methods |
|
// |
|
|
|
public Node cloneNode(boolean deep) { |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
AttrImpl clone = (AttrImpl) super.cloneNode(deep); |
|
|
|
|
|
if (!clone.hasStringValue()) { |
|
|
|
|
|
clone.value = null; |
|
|
|
// Cloning an Attribute always clones its children, |
|
// since they represent its value, no matter whether this |
|
|
|
for (Node child = (Node) value; child != null; |
|
child = child.getNextSibling()) { |
|
clone.appendChild(child.cloneNode(true)); |
|
} |
|
} |
|
clone.isSpecified(true); |
|
return clone; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public short getNodeType() { |
|
return Node.ATTRIBUTE_NODE; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getNodeName() { |
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
return name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setNodeValue(String value) throws DOMException { |
|
setValue(value); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getTypeName() { |
|
return (String)type; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getTypeNamespace() { |
|
if (type != null) { |
|
return DTD_URI; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public TypeInfo getSchemaTypeInfo(){ |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getNodeValue() { |
|
return getValue(); |
|
} |
|
|
|
// |
|
// Attr methods |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
public String getName() { |
|
|
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
return name; |
|
|
|
} // getName():String |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setValue(String newvalue) { |
|
|
|
CoreDocumentImpl ownerDocument = ownerDocument(); |
|
|
|
if (ownerDocument.errorChecking && isReadOnly()) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); |
|
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); |
|
} |
|
|
|
Element ownerElement = getOwnerElement(); |
|
String oldvalue = ""; |
|
TextImpl textNode = null; |
|
|
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
if (value != null) { |
|
if (ownerDocument.getMutationEvents()) { |
|
// Can no longer just discard the kids; they may have |
|
|
|
if (hasStringValue()) { |
|
oldvalue = (String) value; |
|
// create an actual text node as our child so |
|
|
|
textNode = (TextImpl) ownerDocument.createTextNode((String) value); |
|
value = textNode; |
|
textNode.isFirstChild(true); |
|
textNode.previousSibling = textNode; |
|
textNode.ownerNode = this; |
|
textNode.isOwned(true); |
|
hasStringValue(false); |
|
internalRemoveChild(textNode, true); |
|
} |
|
else { |
|
oldvalue = getValue(); |
|
while (value != null) { |
|
internalRemoveChild((Node) value, true); |
|
} |
|
} |
|
} |
|
else { |
|
if (hasStringValue()) { |
|
oldvalue = (String) value; |
|
} |
|
else { |
|
|
|
oldvalue = getValue(); |
|
|
|
ChildNode firstChild = (ChildNode) value; |
|
firstChild.previousSibling = null; |
|
firstChild.isFirstChild(false); |
|
firstChild.ownerNode = ownerDocument; |
|
} |
|
|
|
value = null; |
|
needsSyncChildren(false); |
|
} |
|
if (isIdAttribute() && ownerElement != null) { |
|
ownerDocument.removeIdentifier(oldvalue); |
|
} |
|
} |
|
|
|
// Create and add the new one, generating only non-aggregate events |
|
// (There are no listeners on the new Text, but there may be |
|
// capture/bubble listeners on the Attr. |
|
// Note that aggregate events are NOT dispatched here, |
|
|
|
isSpecified(true); |
|
if (ownerDocument.getMutationEvents()) { |
|
// if there are any event handlers create a real node or |
|
// reuse the one we synthesized for the remove notifications |
|
|
|
if (textNode == null) { |
|
textNode = (TextImpl) ownerDocument.createTextNode(newvalue); |
|
} |
|
else { |
|
textNode.data = newvalue; |
|
} |
|
internalInsertBefore(textNode, null, true); |
|
hasStringValue(false); |
|
|
|
ownerDocument.modifiedAttrValue(this, oldvalue); |
|
} else { |
|
|
|
value = newvalue; |
|
hasStringValue(true); |
|
changed(); |
|
} |
|
if (isIdAttribute() && ownerElement != null) { |
|
ownerDocument.putIdentifier(newvalue, ownerElement); |
|
} |
|
|
|
} // setValue(String) |
|
|
|
|
|
|
|
|
|
*/ |
|
public String getValue() { |
|
|
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
if (value == null) { |
|
return ""; |
|
} |
|
if (hasStringValue()) { |
|
return (String) value; |
|
} |
|
|
|
ChildNode firstChild = ((ChildNode) value); |
|
|
|
String data = null; |
|
if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){ |
|
data = ((EntityReferenceImpl)firstChild).getEntityRefValue(); |
|
} |
|
else { |
|
data = firstChild.getNodeValue(); |
|
} |
|
|
|
ChildNode node = firstChild.nextSibling; |
|
|
|
if (node == null || data == null) return (data == null)?"":data; |
|
|
|
StringBuffer value = new StringBuffer(data); |
|
while (node != null) { |
|
if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE){ |
|
data = ((EntityReferenceImpl)node).getEntityRefValue(); |
|
if (data == null) return ""; |
|
value.append(data); |
|
} |
|
else { |
|
value.append(node.getNodeValue()); |
|
} |
|
node = node.nextSibling; |
|
} |
|
return value.toString(); |
|
|
|
} // getValue():String |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean getSpecified() { |
|
|
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
return isSpecified(); |
|
|
|
} // getSpecified():boolean |
|
|
|
// |
|
// Attr2 methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
public Element getElement() { |
|
// if we have an owner, ownerNode is our ownerElement, otherwise it's |
|
|
|
return (Element) (isOwned() ? ownerNode : null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Element getOwnerElement() { |
|
// if we have an owner, ownerNode is our ownerElement, otherwise it's |
|
|
|
return (Element) (isOwned() ? ownerNode : null); |
|
} |
|
|
|
public void normalize() { |
|
|
|
// No need to normalize if already normalized or |
|
|
|
if (isNormalized() || hasStringValue()) |
|
return; |
|
|
|
Node kid, next; |
|
ChildNode firstChild = (ChildNode)value; |
|
for (kid = firstChild; kid != null; kid = next) { |
|
next = kid.getNextSibling(); |
|
|
|
// If kid is a text node, we need to check for one of two |
|
// conditions: |
|
// 1) There is an adjacent text node |
|
// 2) There is no adjacent text node, but kid is |
|
|
|
if ( kid.getNodeType() == Node.TEXT_NODE ) |
|
{ |
|
|
|
if ( next!=null && next.getNodeType() == Node.TEXT_NODE ) |
|
{ |
|
((Text)kid).appendData(next.getNodeValue()); |
|
removeChild( next ); |
|
next = kid; |
|
} |
|
else |
|
{ |
|
|
|
if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) { |
|
removeChild( kid ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
isNormalized(true); |
|
} // normalize() |
|
|
|
// |
|
// Public methods |
|
// |
|
|
|
|
|
public void setSpecified(boolean arg) { |
|
|
|
if (needsSyncData()) { |
|
synchronizeData(); |
|
} |
|
isSpecified(arg); |
|
|
|
} // setSpecified(boolean) |
|
|
|
|
|
|
|
|
|
*/ |
|
public void setType (Object type){ |
|
this.type = type; |
|
} |
|
|
|
// |
|
// Object methods |
|
// |
|
|
|
|
|
public String toString() { |
|
return getName() + "=" + "\"" + getValue() + "\""; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean hasChildNodes() { |
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
return value != null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public NodeList getChildNodes() { |
|
// JKESS: KNOWN ISSUE HERE |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
return this; |
|
|
|
} // getChildNodes():NodeList |
|
|
|
|
|
public Node getFirstChild() { |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
makeChildNode(); |
|
return (Node) value; |
|
|
|
} // getFirstChild():Node |
|
|
|
|
|
public Node getLastChild() { |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
return lastChild(); |
|
|
|
} // getLastChild():Node |
|
|
|
final ChildNode lastChild() { |
|
|
|
makeChildNode(); |
|
return value != null ? ((ChildNode) value).previousSibling : null; |
|
} |
|
|
|
final void lastChild(ChildNode node) { |
|
|
|
if (value != null) { |
|
((ChildNode) value).previousSibling = node; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node insertBefore(Node newChild, Node refChild) |
|
throws DOMException { |
|
|
|
return internalInsertBefore(newChild, refChild, false); |
|
} // insertBefore(Node,Node):Node |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Node internalInsertBefore(Node newChild, Node refChild, boolean replace) |
|
throws DOMException { |
|
|
|
CoreDocumentImpl ownerDocument = ownerDocument(); |
|
boolean errorChecking = ownerDocument.errorChecking; |
|
|
|
if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { |
|
// SLOW BUT SAFE: We could insert the whole subtree without |
|
// juggling so many next/previous pointers. (Wipe out the |
|
// parent's child-list, patch the parent pointers, set the |
|
// ends of the list.) But we know some subclasses have special- |
|
// case behavior they add to insertBefore(), so we don't risk it. |
|
// This approch also takes fewer bytecodes. |
|
|
|
// NOTE: If one of the children is not a legal child of this |
|
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children |
|
// have been transferred. (Alternative behaviors would be to |
|
// reparent up to the first failure point or reparent all those |
|
// which are acceptable to the target node, neither of which is |
|
// as robust. PR-DOM-0818 isn't entirely clear on which it |
|
// recommends????? |
|
|
|
// No need to check kids for right-document; if they weren't, |
|
|
|
if (errorChecking) { |
|
for (Node kid = newChild.getFirstChild(); |
|
kid != null; kid = kid.getNextSibling()) { |
|
|
|
if (!ownerDocument.isKidOK(this, kid)) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null); |
|
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg); |
|
} |
|
} |
|
} |
|
|
|
while (newChild.hasChildNodes()) { |
|
insertBefore(newChild.getFirstChild(), refChild); |
|
} |
|
return newChild; |
|
} |
|
|
|
if (newChild == refChild) { |
|
|
|
refChild = refChild.getNextSibling(); |
|
removeChild(newChild); |
|
insertBefore(newChild, refChild); |
|
return newChild; |
|
} |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
|
|
if (errorChecking) { |
|
if (isReadOnly()) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); |
|
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); |
|
} |
|
if (newChild.getOwnerDocument() != ownerDocument) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null); |
|
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg); |
|
} |
|
if (!ownerDocument.isKidOK(this, newChild)) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null); |
|
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg); |
|
} |
|
|
|
if (refChild != null && refChild.getParentNode() != this) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); |
|
throw new DOMException(DOMException.NOT_FOUND_ERR, msg); |
|
} |
|
|
|
// Prevent cycles in the tree |
|
// newChild cannot be ancestor of this Node, |
|
|
|
boolean treeSafe = true; |
|
for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode()) |
|
{ |
|
treeSafe = newChild != a; |
|
} |
|
if (!treeSafe) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null); |
|
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg); |
|
} |
|
} |
|
|
|
makeChildNode(); |
|
|
|
|
|
ownerDocument.insertingNode(this, replace); |
|
|
|
|
|
ChildNode newInternal = (ChildNode)newChild; |
|
|
|
Node oldparent = newInternal.parentNode(); |
|
if (oldparent != null) { |
|
oldparent.removeChild(newInternal); |
|
} |
|
|
|
|
|
ChildNode refInternal = (ChildNode) refChild; |
|
|
|
|
|
newInternal.ownerNode = this; |
|
newInternal.isOwned(true); |
|
|
|
// Attach before and after |
|
|
|
ChildNode firstChild = (ChildNode) value; |
|
if (firstChild == null) { |
|
// this our first and only child |
|
value = newInternal; |
|
newInternal.isFirstChild(true); |
|
newInternal.previousSibling = newInternal; |
|
} |
|
else { |
|
if (refInternal == null) { |
|
|
|
ChildNode lastChild = firstChild.previousSibling; |
|
lastChild.nextSibling = newInternal; |
|
newInternal.previousSibling = lastChild; |
|
firstChild.previousSibling = newInternal; |
|
} |
|
else { |
|
|
|
if (refChild == firstChild) { |
|
|
|
firstChild.isFirstChild(false); |
|
newInternal.nextSibling = firstChild; |
|
newInternal.previousSibling = firstChild.previousSibling; |
|
firstChild.previousSibling = newInternal; |
|
value = newInternal; |
|
newInternal.isFirstChild(true); |
|
} |
|
else { |
|
|
|
ChildNode prev = refInternal.previousSibling; |
|
newInternal.nextSibling = refInternal; |
|
prev.nextSibling = newInternal; |
|
refInternal.previousSibling = newInternal; |
|
newInternal.previousSibling = prev; |
|
} |
|
} |
|
} |
|
|
|
changed(); |
|
|
|
|
|
ownerDocument.insertedNode(this, newInternal, replace); |
|
|
|
checkNormalizationAfterInsert(newInternal); |
|
|
|
return newChild; |
|
|
|
} // internalInsertBefore(Node,Node,int):Node |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node removeChild(Node oldChild) |
|
throws DOMException { |
|
|
|
if (hasStringValue()) { |
|
|
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); |
|
throw new DOMException(DOMException.NOT_FOUND_ERR, msg); |
|
} |
|
return internalRemoveChild(oldChild, false); |
|
} // removeChild(Node) :Node |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Node internalRemoveChild(Node oldChild, boolean replace) |
|
throws DOMException { |
|
|
|
CoreDocumentImpl ownerDocument = ownerDocument(); |
|
if (ownerDocument.errorChecking) { |
|
if (isReadOnly()) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); |
|
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); |
|
} |
|
if (oldChild != null && oldChild.getParentNode() != this) { |
|
String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); |
|
throw new DOMException(DOMException.NOT_FOUND_ERR, msg); |
|
} |
|
} |
|
|
|
ChildNode oldInternal = (ChildNode) oldChild; |
|
|
|
|
|
ownerDocument.removingNode(this, oldInternal, replace); |
|
|
|
// Patch linked list around oldChild |
|
|
|
if (oldInternal == value) { // oldInternal == firstChild |
|
|
|
oldInternal.isFirstChild(false); |
|
|
|
value = oldInternal.nextSibling; |
|
ChildNode firstChild = (ChildNode) value; |
|
if (firstChild != null) { |
|
firstChild.isFirstChild(true); |
|
firstChild.previousSibling = oldInternal.previousSibling; |
|
} |
|
} else { |
|
ChildNode prev = oldInternal.previousSibling; |
|
ChildNode next = oldInternal.nextSibling; |
|
prev.nextSibling = next; |
|
if (next == null) { |
|
|
|
ChildNode firstChild = (ChildNode) value; |
|
firstChild.previousSibling = prev; |
|
} else { |
|
|
|
next.previousSibling = prev; |
|
} |
|
} |
|
|
|
|
|
ChildNode oldPreviousSibling = oldInternal.previousSibling(); |
|
|
|
|
|
oldInternal.ownerNode = ownerDocument; |
|
oldInternal.isOwned(false); |
|
oldInternal.nextSibling = null; |
|
oldInternal.previousSibling = null; |
|
|
|
changed(); |
|
|
|
|
|
ownerDocument.removedNode(this, replace); |
|
|
|
checkNormalizationAfterRemove(oldPreviousSibling); |
|
|
|
return oldInternal; |
|
|
|
} // internalRemoveChild(Node,int):Node |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node replaceChild(Node newChild, Node oldChild) |
|
throws DOMException { |
|
|
|
makeChildNode(); |
|
|
|
// If Mutation Events are being generated, this operation might |
|
// throw aggregate events twice when modifying an Attr -- once |
|
// on insertion and once on removal. DOM Level 2 does not specify |
|
// this as either desirable or undesirable, but hints that |
|
// aggregations should be issued only once per user request. |
|
|
|
|
|
CoreDocumentImpl ownerDocument = ownerDocument(); |
|
ownerDocument.replacingNode(this); |
|
|
|
internalInsertBefore(newChild, oldChild, true); |
|
if (newChild != oldChild) { |
|
internalRemoveChild(oldChild, true); |
|
} |
|
|
|
|
|
ownerDocument.replacedNode(this); |
|
|
|
return oldChild; |
|
} |
|
|
|
// |
|
// NodeList methods |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
public int getLength() { |
|
|
|
if (hasStringValue()) { |
|
return 1; |
|
} |
|
ChildNode node = (ChildNode) value; |
|
int length = 0; |
|
for (; node != null; node = node.nextSibling) { |
|
length++; |
|
} |
|
return length; |
|
|
|
} // getLength():int |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Node item(int index) { |
|
|
|
if (hasStringValue()) { |
|
if (index != 0 || value == null) { |
|
return null; |
|
} |
|
else { |
|
makeChildNode(); |
|
return (Node) value; |
|
} |
|
} |
|
if (index < 0) { |
|
return null; |
|
} |
|
ChildNode node = (ChildNode) value; |
|
for (int i = 0; i < index && node != null; i++) { |
|
node = node.nextSibling; |
|
} |
|
return node; |
|
|
|
} // item(int):Node |
|
|
|
// |
|
// DOM3 |
|
// |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isEqualNode(Node arg) { |
|
return super.isEqualNode(arg); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isDerivedFrom(String typeNamespaceArg, |
|
String typeNameArg, |
|
int derivationMethod) { |
|
|
|
return false; |
|
} |
|
|
|
|
|
// |
|
// Public methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setReadOnly(boolean readOnly, boolean deep) { |
|
|
|
super.setReadOnly(readOnly, deep); |
|
|
|
if (deep) { |
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
|
|
if (hasStringValue()) { |
|
return; |
|
} |
|
|
|
for (ChildNode mykid = (ChildNode) value; |
|
mykid != null; |
|
mykid = mykid.nextSibling) { |
|
if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) { |
|
mykid.setReadOnly(readOnly,true); |
|
} |
|
} |
|
} |
|
} // setReadOnly(boolean,boolean) |
|
|
|
// |
|
// Protected methods |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
protected void synchronizeChildren() { |
|
|
|
needsSyncChildren(false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void checkNormalizationAfterInsert(ChildNode insertedChild) { |
|
|
|
if (insertedChild.getNodeType() == Node.TEXT_NODE) { |
|
ChildNode prev = insertedChild.previousSibling(); |
|
ChildNode next = insertedChild.nextSibling; |
|
// If an adjacent sibling of the new child is a text node, |
|
|
|
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) || |
|
(next != null && next.getNodeType() == Node.TEXT_NODE)) { |
|
isNormalized(false); |
|
} |
|
} |
|
else { |
|
// If the new child is not normalized, |
|
|
|
if (!insertedChild.isNormalized()) { |
|
isNormalized(false); |
|
} |
|
} |
|
} // checkNormalizationAfterInsert(ChildNode) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void checkNormalizationAfterRemove(ChildNode previousSibling) { |
|
// See if removal caused this node to be unnormalized. |
|
// If the adjacent siblings of the removed child were both text nodes, |
|
|
|
if (previousSibling != null && |
|
previousSibling.getNodeType() == Node.TEXT_NODE) { |
|
|
|
ChildNode next = previousSibling.nextSibling; |
|
if (next != null && next.getNodeType() == Node.TEXT_NODE) { |
|
isNormalized(false); |
|
} |
|
} |
|
} // checkNormalizationAfterRemove(ChildNode) |
|
|
|
// |
|
// Serialization methods |
|
// |
|
|
|
|
|
private void writeObject(ObjectOutputStream out) throws IOException { |
|
|
|
|
|
if (needsSyncChildren()) { |
|
synchronizeChildren(); |
|
} |
|
|
|
out.defaultWriteObject(); |
|
|
|
} // writeObject(ObjectOutputStream) |
|
|
|
|
|
private void readObject(ObjectInputStream ois) |
|
throws ClassNotFoundException, IOException { |
|
|
|
|
|
ois.defaultReadObject(); |
|
|
|
// hardset synchildren - so we don't try to sync - |
|
// it does not make any sense to try to synchildren when we just |
|
|
|
needsSyncChildren(false); |
|
|
|
} // readObject(ObjectInputStream) |
|
|
|
|
|
} // class AttrImpl |