0001: /*
0002: * The Apache Software License, Version 1.1
0003: *
0004: *
0005: * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
0006: * reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Apache Software Foundation (http://www.apache.org/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Xerces" and "Apache Software Foundation" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact apache@apache.org.
0031: *
0032: * 5. Products derived from this software may not be called "Apache",
0033: * nor may "Apache" appear in their name, without prior written
0034: * permission of the Apache Software Foundation.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * This software consists of voluntary contributions made by many
0051: * individuals on behalf of the Apache Software Foundation and was
0052: * originally based on software copyright (c) 1999, International
0053: * Business Machines, Inc., http://www.apache.org. For more
0054: * information on the Apache Software Foundation, please see
0055: * <http://www.apache.org/>.
0056: */
0057:
0058: package org.apache.xerces.dom;
0059:
0060: import java.io.IOException;
0061: import java.io.ObjectInputStream;
0062: import java.io.ObjectOutputStream;
0063:
0064: import org.w3c.dom.Attr;
0065: import org.w3c.dom.DOMException;
0066: import org.w3c.dom.Element;
0067: import org.w3c.dom.Node;
0068: import org.w3c.dom.NodeList;
0069: import org.w3c.dom.Text;
0070:
0071: /**
0072: * Attribute represents an XML-style attribute of an
0073: * Element. Typically, the allowable values are controlled by its
0074: * declaration in the Document Type Definition (DTD) governing this
0075: * kind of document.
0076: * <P>
0077: * If the attribute has not been explicitly assigned a value, but has
0078: * been declared in the DTD, it will exist and have that default. Only
0079: * if neither the document nor the DTD specifies a value will the
0080: * Attribute really be considered absent and have no value; in that
0081: * case, querying the attribute will return null.
0082: * <P>
0083: * Attributes may have multiple children that contain their data. (XML
0084: * allows attributes to contain entity references, and tokenized
0085: * attribute types such as NMTOKENS may have a child for each token.)
0086: * For convenience, the Attribute object's getValue() method returns
0087: * the string version of the attribute's value.
0088: * <P>
0089: * Attributes are not children of the Elements they belong to, in the
0090: * usual sense, and have no valid Parent reference. However, the spec
0091: * says they _do_ belong to a specific Element, and an INUSE exception
0092: * is to be thrown if the user attempts to explicitly share them
0093: * between elements.
0094: * <P>
0095: * Note that Elements do not permit attributes to appear to be shared
0096: * (see the INUSE exception), so this object's mutability is
0097: * officially not an issue.
0098: * <p>
0099: * Note: The ownerNode attribute is used to store the Element the Attr
0100: * node is associated with. Attr nodes do not have parent nodes.
0101: * Besides, the getOwnerElement() method can be used to get the element node
0102: * this attribute is associated with.
0103: * <P>
0104: * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
0105: * it, does.
0106: *
0107: * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
0108: * NodeImpl and provide its own implementation of the ParentNode's behavior.
0109: * The reason is that we now try and avoid to always create a Text node to
0110: * hold the value of an attribute. The DOM spec requires it, so we still have
0111: * to do it in case getFirstChild() is called for instance. The reason
0112: * attribute values are stored as a list of nodes is so that they can carry
0113: * more than a simple string. They can also contain EntityReference nodes.
0114: * However, most of the times people only have a single string that they only
0115: * set and get through Element.set/getAttribute or Attr.set/getValue. In this
0116: * new version, the Attr node has a value pointer which can either be the
0117: * String directly or a pointer to the first ChildNode. A flag tells which one
0118: * it currently is. Note that while we try to stick with the direct String as
0119: * much as possible once we've switched to a node there is no going back. This
0120: * is because we have no way to know whether the application keeps referring to
0121: * the node we once returned.
0122: * <p> The gain in memory varies on the density of attributes in the document.
0123: * But in the tests I've run I've seen up to 12% of memory gain. And the good
0124: * thing is that it also leads to a slight gain in speed because we allocate
0125: * fewer objects! I mean, that's until we have to actually create the node...
0126: * <p>
0127: * To avoid too much duplicated code, I got rid of ParentNode and renamed
0128: * ChildAndParentNode, which I never really liked, to ParentNode for
0129: * simplicity, this doesn't make much of a difference in memory usage because
0130: * there are only very few objects that are only a Parent. This is only true
0131: * now because AttrImpl now inherits directly from NodeImpl and has its own
0132: * implementation of the ParentNode's node behavior. So there is still some
0133: * duplicated code there.
0134: * <p>
0135: * This class doesn't directly support mutation events, however, it notifies
0136: * the document when mutations are performed so that the document class do so.
0137: *
0138: * <p><b>WARNING</b>: Some of the code here is partially duplicated in
0139: * ParentNode, be careful to keep these two classes in sync!
0140: *
0141: * @see AttrNSImpl
0142: *
0143: * @author Arnaud Le Hors, IBM
0144: * @author Joe Kesselman, IBM
0145: * @author Andy Clark, IBM
0146: * @version
0147: * @since PR-DOM-Level-1-19980818.
0148: *
0149: */
0150: public class AttrImpl extends NodeImpl implements Attr {
0151:
0152: //
0153: // Constants
0154: //
0155:
0156: /** Serialization version. */
0157: static final long serialVersionUID = 7277707688218972102L;
0158:
0159: //
0160: // Data
0161: //
0162:
0163: /** This can either be a String or the first child node. */
0164: protected Object value = null;
0165:
0166: /** Attribute name. */
0167: protected String name;
0168:
0169: protected static TextImpl textNode = null;
0170:
0171: //
0172: // Constructors
0173: //
0174:
0175: /**
0176: * Attribute has no public constructor. Please use the factory
0177: * method in the Document class.
0178: */
0179: protected AttrImpl(CoreDocumentImpl ownerDocument, String name) {
0180: super (ownerDocument);
0181: this .name = name;
0182: /** False for default attributes. */
0183: isSpecified(true);
0184: hasStringValue(true);
0185: }
0186:
0187: // for AttrNSImpl
0188: protected AttrImpl() {
0189: }
0190:
0191: // create a real text node as child if we don't have one yet
0192: protected void makeChildNode() {
0193: if (hasStringValue()) {
0194: if (value != null) {
0195: TextImpl text = (TextImpl) ownerDocument()
0196: .createTextNode((String) value);
0197: value = text;
0198: text.isFirstChild(true);
0199: text.previousSibling = text;
0200: text.ownerNode = this ;
0201: text.isOwned(true);
0202: }
0203: hasStringValue(false);
0204: }
0205: }
0206:
0207: /**
0208: * NON-DOM
0209: * set the ownerDocument of this node and its children
0210: */
0211: void setOwnerDocument(CoreDocumentImpl doc) {
0212: if (needsSyncChildren()) {
0213: synchronizeChildren();
0214: }
0215: super .setOwnerDocument(doc);
0216: if (!hasStringValue()) {
0217: for (ChildNode child = (ChildNode) value; child != null; child = child.nextSibling) {
0218: child.setOwnerDocument(doc);
0219: }
0220: }
0221: }
0222:
0223: //
0224: // Node methods
0225: //
0226:
0227: public Node cloneNode(boolean deep) {
0228:
0229: if (needsSyncChildren()) {
0230: synchronizeChildren();
0231: }
0232: AttrImpl clone = (AttrImpl) super .cloneNode(deep);
0233:
0234: // take care of case where there are kids
0235: if (!clone.hasStringValue()) {
0236:
0237: // Need to break the association w/ original kids
0238: clone.value = null;
0239:
0240: // Then, if deep, clone the kids too.
0241: if (deep) {
0242: for (Node child = (Node) value; child != null; child = child
0243: .getNextSibling()) {
0244: clone.appendChild(child.cloneNode(true));
0245: }
0246: }
0247: }
0248: clone.isSpecified(true);
0249: return clone;
0250: }
0251:
0252: /**
0253: * A short integer indicating what type of node this is. The named
0254: * constants for this value are defined in the org.w3c.dom.Node interface.
0255: */
0256: public short getNodeType() {
0257: return Node.ATTRIBUTE_NODE;
0258: }
0259:
0260: /**
0261: * Returns the attribute name
0262: */
0263: public String getNodeName() {
0264: if (needsSyncData()) {
0265: synchronizeData();
0266: }
0267: return name;
0268: }
0269:
0270: /**
0271: * Implicit in the rerouting of getNodeValue to getValue is the
0272: * need to redefine setNodeValue, for symmetry's sake. Note that
0273: * since we're explicitly providing a value, Specified should be set
0274: * true.... even if that value equals the default.
0275: */
0276: public void setNodeValue(String value) throws DOMException {
0277: setValue(value);
0278: }
0279:
0280: /**
0281: * In Attribute objects, NodeValue is considered a synonym for
0282: * Value.
0283: *
0284: * @see #getValue()
0285: */
0286: public String getNodeValue() {
0287: return getValue();
0288: }
0289:
0290: //
0291: // Attr methods
0292: //
0293:
0294: /**
0295: * In Attributes, NodeName is considered a synonym for the
0296: * attribute's Name
0297: */
0298: public String getName() {
0299:
0300: if (needsSyncData()) {
0301: synchronizeData();
0302: }
0303: return name;
0304:
0305: } // getName():String
0306:
0307: /**
0308: * The DOM doesn't clearly define what setValue(null) means. I've taken it
0309: * as "remove all children", which from outside should appear
0310: * similar to setting it to the empty string.
0311: */
0312: public void setValue(String newvalue) {
0313:
0314: if (isReadOnly()) {
0315: throw new DOMException(
0316: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0317: "DOM001 Modification not allowed");
0318: }
0319: CoreDocumentImpl ownerDocument = ownerDocument();
0320: String oldvalue = "";
0321: if (ownerDocument.getMutationEvents()) {
0322: // Can no longer just discard the kids; they may have
0323: // event listeners waiting for them to disconnect.
0324: if (needsSyncChildren()) {
0325: synchronizeChildren();
0326: }
0327: if (value != null) {
0328: if (hasStringValue()) {
0329: oldvalue = (String) value;
0330: // create an actual text node as our child so
0331: // that we can use it in the event
0332: if (textNode == null) {
0333: textNode = (TextImpl) ownerDocument
0334: .createTextNode((String) value);
0335: } else {
0336: textNode.data = (String) value;
0337: }
0338: value = textNode;
0339: textNode.isFirstChild(true);
0340: textNode.previousSibling = textNode;
0341: textNode.ownerNode = this ;
0342: textNode.isOwned(true);
0343: hasStringValue(false);
0344: internalRemoveChild(textNode, true);
0345: } else {
0346: oldvalue = getValue();
0347: while (value != null) {
0348: internalRemoveChild((Node) value, true);
0349: }
0350: }
0351: }
0352: } else {
0353: // simply discard children if any
0354: if (!hasStringValue() && value != null) {
0355: // remove ref from first child to last child
0356: ChildNode firstChild = (ChildNode) value;
0357: firstChild.previousSibling = null;
0358: firstChild.isFirstChild(false);
0359: }
0360: // then remove ref to current value
0361: value = null;
0362: needsSyncChildren(false);
0363: }
0364:
0365: // Create and add the new one, generating only non-aggregate events
0366: // (There are no listeners on the new Text, but there may be
0367: // capture/bubble listeners on the Attr.
0368: // Note that aggregate events are NOT dispatched here,
0369: // since we need to combine the remove and insert.
0370: isSpecified(true);
0371: if (ownerDocument.getMutationEvents()) {
0372: // if there are any event handlers create a real node
0373: internalInsertBefore(
0374: ownerDocument.createTextNode(newvalue), null, true);
0375: hasStringValue(false);
0376: // notify document
0377: ownerDocument.modifiedAttrValue(this , oldvalue);
0378: } else {
0379: // directly store the string
0380: value = newvalue;
0381: hasStringValue(true);
0382: changed();
0383: }
0384:
0385: } // setValue(String)
0386:
0387: /**
0388: * The "string value" of an Attribute is its text representation,
0389: * which in turn is a concatenation of the string values of its children.
0390: */
0391: public String getValue() {
0392:
0393: if (needsSyncChildren()) {
0394: synchronizeChildren();
0395: }
0396: if (value == null) {
0397: return "";
0398: }
0399: if (hasStringValue()) {
0400: return (String) value;
0401: }
0402: ChildNode firstChild = ((ChildNode) value);
0403: ChildNode node = firstChild.nextSibling;
0404: if (node == null) {
0405: return firstChild.getNodeValue();
0406: }
0407: StringBuffer value = new StringBuffer(firstChild.getNodeValue());
0408: while (node != null) {
0409: value.append(node.getNodeValue());
0410: node = node.nextSibling;
0411: }
0412: return value.toString();
0413:
0414: } // getValue():String
0415:
0416: /**
0417: * The "specified" flag is true if and only if this attribute's
0418: * value was explicitly specified in the original document. Note that
0419: * the implementation, not the user, is in charge of this
0420: * property. If the user asserts an Attribute value (even if it ends
0421: * up having the same value as the default), it is considered a
0422: * specified attribute. If you really want to revert to the default,
0423: * delete the attribute from the Element, and the Implementation will
0424: * re-assert the default (if any) in its place, with the appropriate
0425: * specified=false setting.
0426: */
0427: public boolean getSpecified() {
0428:
0429: if (needsSyncData()) {
0430: synchronizeData();
0431: }
0432: return isSpecified();
0433:
0434: } // getSpecified():boolean
0435:
0436: //
0437: // Attr2 methods
0438: //
0439:
0440: /**
0441: * Returns the element node that this attribute is associated with,
0442: * or null if the attribute has not been added to an element.
0443: *
0444: * @see #getOwnerElement
0445: *
0446: * @deprecated Previous working draft of DOM Level 2. New method
0447: * is <tt>getOwnerElement()</tt>.
0448: */
0449: public Element getElement() {
0450: // if we have an owner, ownerNode is our ownerElement, otherwise it's
0451: // our ownerDocument and we don't have an ownerElement
0452: return (Element) (isOwned() ? ownerNode : null);
0453: }
0454:
0455: /**
0456: * Returns the element node that this attribute is associated with,
0457: * or null if the attribute has not been added to an element.
0458: *
0459: * @since WD-DOM-Level-2-19990719
0460: */
0461: public Element getOwnerElement() {
0462: // if we have an owner, ownerNode is our ownerElement, otherwise it's
0463: // our ownerDocument and we don't have an ownerElement
0464: return (Element) (isOwned() ? ownerNode : null);
0465: }
0466:
0467: public void normalize() {
0468:
0469: // No need to normalize if already normalized or
0470: // if value is kept as a String.
0471: if (isNormalized() || hasStringValue())
0472: return;
0473:
0474: Node kid, next;
0475: ChildNode firstChild = (ChildNode) value;
0476: for (kid = firstChild; kid != null; kid = next) {
0477: next = kid.getNextSibling();
0478:
0479: // If kid is a text node, we need to check for one of two
0480: // conditions:
0481: // 1) There is an adjacent text node
0482: // 2) There is no adjacent text node, but kid is
0483: // an empty text node.
0484: if (kid.getNodeType() == Node.TEXT_NODE) {
0485: // If an adjacent text node, merge it with kid
0486: if (next != null
0487: && next.getNodeType() == Node.TEXT_NODE) {
0488: ((Text) kid).appendData(next.getNodeValue());
0489: removeChild(next);
0490: next = kid; // Don't advance; there might be another.
0491: } else {
0492: // If kid is empty, remove it
0493: if (kid.getNodeValue().length() == 0)
0494: removeChild(kid);
0495: }
0496: }
0497: }
0498:
0499: isNormalized(true);
0500: } // normalize()
0501:
0502: //
0503: // Public methods
0504: //
0505:
0506: /** NON-DOM, for use by parser */
0507: public void setSpecified(boolean arg) {
0508:
0509: if (needsSyncData()) {
0510: synchronizeData();
0511: }
0512: isSpecified(arg);
0513:
0514: } // setSpecified(boolean)
0515:
0516: //
0517: // Object methods
0518: //
0519:
0520: /** NON-DOM method for debugging convenience */
0521: public String toString() {
0522: return getName() + "=" + "\"" + getValue() + "\"";
0523: }
0524:
0525: /**
0526: * Test whether this node has any children. Convenience shorthand
0527: * for (Node.getFirstChild()!=null)
0528: */
0529: public boolean hasChildNodes() {
0530: if (needsSyncChildren()) {
0531: synchronizeChildren();
0532: }
0533: return value != null;
0534: }
0535:
0536: /**
0537: * Obtain a NodeList enumerating all children of this node. If there
0538: * are none, an (initially) empty NodeList is returned.
0539: * <p>
0540: * NodeLists are "live"; as children are added/removed the NodeList
0541: * will immediately reflect those changes. Also, the NodeList refers
0542: * to the actual nodes, so changes to those nodes made via the DOM tree
0543: * will be reflected in the NodeList and vice versa.
0544: * <p>
0545: * In this implementation, Nodes implement the NodeList interface and
0546: * provide their own getChildNodes() support. Other DOMs may solve this
0547: * differently.
0548: */
0549: public NodeList getChildNodes() {
0550: // JKESS: KNOWN ISSUE HERE
0551:
0552: if (needsSyncChildren()) {
0553: synchronizeChildren();
0554: }
0555: return this ;
0556:
0557: } // getChildNodes():NodeList
0558:
0559: /** The first child of this Node, or null if none. */
0560: public Node getFirstChild() {
0561:
0562: if (needsSyncChildren()) {
0563: synchronizeChildren();
0564: }
0565: makeChildNode();
0566: return (Node) value;
0567:
0568: } // getFirstChild():Node
0569:
0570: /** The last child of this Node, or null if none. */
0571: public Node getLastChild() {
0572:
0573: if (needsSyncChildren()) {
0574: synchronizeChildren();
0575: }
0576: return lastChild();
0577:
0578: } // getLastChild():Node
0579:
0580: final ChildNode lastChild() {
0581: // last child is stored as the previous sibling of first child
0582: makeChildNode();
0583: return value != null ? ((ChildNode) value).previousSibling
0584: : null;
0585: }
0586:
0587: final void lastChild(ChildNode node) {
0588: // store lastChild as previous sibling of first child
0589: if (value != null) {
0590: ((ChildNode) value).previousSibling = node;
0591: }
0592: }
0593:
0594: /**
0595: * Move one or more node(s) to our list of children. Note that this
0596: * implicitly removes them from their previous parent.
0597: *
0598: * @param newChild The Node to be moved to our subtree. As a
0599: * convenience feature, inserting a DocumentNode will instead insert
0600: * all its children.
0601: *
0602: * @param refChild Current child which newChild should be placed
0603: * immediately before. If refChild is null, the insertion occurs
0604: * after all existing Nodes, like appendChild().
0605: *
0606: * @returns newChild, in its new state (relocated, or emptied in the
0607: * case of DocumentNode.)
0608: *
0609: * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
0610: * type that shouldn't be a child of this node, or if newChild is an
0611: * ancestor of this node.
0612: *
0613: * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
0614: * different owner document than we do.
0615: *
0616: * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
0617: * this node.
0618: *
0619: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
0620: * read-only.
0621: */
0622: public Node insertBefore(Node newChild, Node refChild)
0623: throws DOMException {
0624: // Tail-call; optimizer should be able to do good things with.
0625: return internalInsertBefore(newChild, refChild, false);
0626: } // insertBefore(Node,Node):Node
0627:
0628: /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
0629: * to control which mutation events are spawned. This version of the
0630: * insertBefore operation allows us to do so. It is not intended
0631: * for use by application programs.
0632: */
0633: Node internalInsertBefore(Node newChild, Node refChild,
0634: boolean replace) throws DOMException {
0635:
0636: CoreDocumentImpl ownerDocument = ownerDocument();
0637: boolean errorChecking = ownerDocument.errorChecking;
0638:
0639: if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
0640: // SLOW BUT SAFE: We could insert the whole subtree without
0641: // juggling so many next/previous pointers. (Wipe out the
0642: // parent's child-list, patch the parent pointers, set the
0643: // ends of the list.) But we know some subclasses have special-
0644: // case behavior they add to insertBefore(), so we don't risk it.
0645: // This approch also takes fewer bytecodes.
0646:
0647: // NOTE: If one of the children is not a legal child of this
0648: // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
0649: // have been transferred. (Alternative behaviors would be to
0650: // reparent up to the first failure point or reparent all those
0651: // which are acceptable to the target node, neither of which is
0652: // as robust. PR-DOM-0818 isn't entirely clear on which it
0653: // recommends?????
0654:
0655: // No need to check kids for right-document; if they weren't,
0656: // they wouldn't be kids of that DocFrag.
0657: if (errorChecking) {
0658: for (Node kid = newChild.getFirstChild(); // Prescan
0659: kid != null; kid = kid.getNextSibling()) {
0660:
0661: if (!ownerDocument.isKidOK(this , kid)) {
0662: throw new DOMException(
0663: DOMException.HIERARCHY_REQUEST_ERR,
0664: "DOM006 Hierarchy request error");
0665: }
0666: }
0667: }
0668:
0669: while (newChild.hasChildNodes()) {
0670: insertBefore(newChild.getFirstChild(), refChild);
0671: }
0672: return newChild;
0673: }
0674:
0675: if (newChild == refChild) {
0676: // stupid case that must be handled as a no-op triggering events...
0677: refChild = refChild.getNextSibling();
0678: removeChild(newChild);
0679: insertBefore(newChild, refChild);
0680: return newChild;
0681: }
0682:
0683: if (needsSyncChildren()) {
0684: synchronizeChildren();
0685: }
0686:
0687: if (errorChecking) {
0688: if (isReadOnly()) {
0689: throw new DOMException(
0690: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0691: "DOM001 Modification not allowed");
0692: }
0693: if (newChild.getOwnerDocument() != ownerDocument) {
0694: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
0695: "DOM005 Wrong document");
0696: }
0697: if (!ownerDocument.isKidOK(this , newChild)) {
0698: throw new DOMException(
0699: DOMException.HIERARCHY_REQUEST_ERR,
0700: "DOM006 Hierarchy request error");
0701: }
0702: // refChild must be a child of this node (or null)
0703: if (refChild != null && refChild.getParentNode() != this ) {
0704: throw new DOMException(DOMException.NOT_FOUND_ERR,
0705: "DOM008 Not found");
0706: }
0707:
0708: // Prevent cycles in the tree
0709: // newChild cannot be ancestor of this Node,
0710: // and actually cannot be this
0711: boolean treeSafe = true;
0712: for (NodeImpl a = this ; treeSafe && a != null; a = a
0713: .parentNode()) {
0714: treeSafe = newChild != a;
0715: }
0716: if (!treeSafe) {
0717: throw new DOMException(
0718: DOMException.HIERARCHY_REQUEST_ERR,
0719: "DOM006 Hierarchy request error");
0720: }
0721: }
0722:
0723: makeChildNode(); // make sure we have a node and not a string
0724:
0725: // notify document
0726: ownerDocument.insertingNode(this , replace);
0727:
0728: // Convert to internal type, to avoid repeated casting
0729: ChildNode newInternal = (ChildNode) newChild;
0730:
0731: Node oldparent = newInternal.parentNode();
0732: if (oldparent != null) {
0733: oldparent.removeChild(newInternal);
0734: }
0735:
0736: // Convert to internal type, to avoid repeated casting
0737: ChildNode refInternal = (ChildNode) refChild;
0738:
0739: // Attach up
0740: newInternal.ownerNode = this ;
0741: newInternal.isOwned(true);
0742:
0743: // Attach before and after
0744: // Note: firstChild.previousSibling == lastChild!!
0745: ChildNode firstChild = (ChildNode) value;
0746: if (firstChild == null) {
0747: // this our first and only child
0748: value = newInternal; // firstchild = newInternal;
0749: newInternal.isFirstChild(true);
0750: newInternal.previousSibling = newInternal;
0751: } else {
0752: if (refInternal == null) {
0753: // this is an append
0754: ChildNode lastChild = firstChild.previousSibling;
0755: lastChild.nextSibling = newInternal;
0756: newInternal.previousSibling = lastChild;
0757: firstChild.previousSibling = newInternal;
0758: } else {
0759: // this is an insert
0760: if (refChild == firstChild) {
0761: // at the head of the list
0762: firstChild.isFirstChild(false);
0763: newInternal.nextSibling = firstChild;
0764: newInternal.previousSibling = firstChild.previousSibling;
0765: firstChild.previousSibling = newInternal;
0766: value = newInternal; // firstChild = newInternal;
0767: newInternal.isFirstChild(true);
0768: } else {
0769: // somewhere in the middle
0770: ChildNode prev = refInternal.previousSibling;
0771: newInternal.nextSibling = refInternal;
0772: prev.nextSibling = newInternal;
0773: refInternal.previousSibling = newInternal;
0774: newInternal.previousSibling = prev;
0775: }
0776: }
0777: }
0778:
0779: changed();
0780:
0781: // notify document
0782: ownerDocument.insertedNode(this , newInternal, replace);
0783:
0784: checkNormalizationAfterInsert(newInternal);
0785:
0786: return newChild;
0787:
0788: } // internalInsertBefore(Node,Node,int):Node
0789:
0790: /**
0791: * Remove a child from this Node. The removed child's subtree
0792: * remains intact so it may be re-inserted elsewhere.
0793: *
0794: * @return oldChild, in its new state (removed).
0795: *
0796: * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
0797: * this node.
0798: *
0799: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
0800: * read-only.
0801: */
0802: public Node removeChild(Node oldChild) throws DOMException {
0803: // Tail-call, should be optimizable
0804: if (hasStringValue()) {
0805: // we don't have any child per say so it can't be one of them!
0806: throw new DOMException(DOMException.NOT_FOUND_ERR,
0807: "DOM008 Not found");
0808: }
0809: return internalRemoveChild(oldChild, false);
0810: } // removeChild(Node) :Node
0811:
0812: /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
0813: * to control which mutation events are spawned. This version of the
0814: * removeChild operation allows us to do so. It is not intended
0815: * for use by application programs.
0816: */
0817: Node internalRemoveChild(Node oldChild, boolean replace)
0818: throws DOMException {
0819:
0820: CoreDocumentImpl ownerDocument = ownerDocument();
0821: if (ownerDocument.errorChecking) {
0822: if (isReadOnly()) {
0823: throw new DOMException(
0824: DOMException.NO_MODIFICATION_ALLOWED_ERR,
0825: "DOM001 Modification not allowed");
0826: }
0827: if (oldChild != null && oldChild.getParentNode() != this ) {
0828: throw new DOMException(DOMException.NOT_FOUND_ERR,
0829: "DOM008 Not found");
0830: }
0831: }
0832:
0833: ChildNode oldInternal = (ChildNode) oldChild;
0834:
0835: // notify document
0836: ownerDocument.removingNode(this , oldInternal, replace);
0837:
0838: // Patch linked list around oldChild
0839: // Note: lastChild == firstChild.previousSibling
0840: if (oldInternal == value) { // oldInternal == firstChild
0841: // removing first child
0842: oldInternal.isFirstChild(false);
0843: // next line is: firstChild = oldInternal.nextSibling
0844: value = oldInternal.nextSibling;
0845: ChildNode firstChild = (ChildNode) value;
0846: if (firstChild != null) {
0847: firstChild.isFirstChild(true);
0848: firstChild.previousSibling = oldInternal.previousSibling;
0849: }
0850: } else {
0851: ChildNode prev = oldInternal.previousSibling;
0852: ChildNode next = oldInternal.nextSibling;
0853: prev.nextSibling = next;
0854: if (next == null) {
0855: // removing last child
0856: ChildNode firstChild = (ChildNode) value;
0857: firstChild.previousSibling = prev;
0858: } else {
0859: // removing some other child in the middle
0860: next.previousSibling = prev;
0861: }
0862: }
0863:
0864: // Save previous sibling for normalization checking.
0865: ChildNode oldPreviousSibling = oldInternal.previousSibling();
0866:
0867: // Remove oldInternal's references to tree
0868: oldInternal.ownerNode = ownerDocument;
0869: oldInternal.isOwned(false);
0870: oldInternal.nextSibling = null;
0871: oldInternal.previousSibling = null;
0872:
0873: changed();
0874:
0875: // notify document
0876: ownerDocument.removedNode(this , replace);
0877:
0878: checkNormalizationAfterRemove(oldPreviousSibling);
0879:
0880: return oldInternal;
0881:
0882: } // internalRemoveChild(Node,int):Node
0883:
0884: /**
0885: * Make newChild occupy the location that oldChild used to
0886: * have. Note that newChild will first be removed from its previous
0887: * parent, if any. Equivalent to inserting newChild before oldChild,
0888: * then removing oldChild.
0889: *
0890: * @returns oldChild, in its new state (removed).
0891: *
0892: * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
0893: * type that shouldn't be a child of this node, or if newChild is
0894: * one of our ancestors.
0895: *
0896: * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
0897: * different owner document than we do.
0898: *
0899: * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
0900: * this node.
0901: *
0902: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
0903: * read-only.
0904: */
0905: public Node replaceChild(Node newChild, Node oldChild)
0906: throws DOMException {
0907:
0908: makeChildNode();
0909:
0910: // If Mutation Events are being generated, this operation might
0911: // throw aggregate events twice when modifying an Attr -- once
0912: // on insertion and once on removal. DOM Level 2 does not specify
0913: // this as either desirable or undesirable, but hints that
0914: // aggregations should be issued only once per user request.
0915:
0916: // notify document
0917: CoreDocumentImpl ownerDocument = ownerDocument();
0918: ownerDocument.replacingNode(this );
0919:
0920: internalInsertBefore(newChild, oldChild, true);
0921: if (newChild != oldChild) {
0922: internalRemoveChild(oldChild, true);
0923: }
0924:
0925: // notify document
0926: ownerDocument.replacedNode(this );
0927:
0928: return oldChild;
0929: }
0930:
0931: //
0932: // NodeList methods
0933: //
0934:
0935: /**
0936: * NodeList method: Count the immediate children of this node
0937: * @return int
0938: */
0939: public int getLength() {
0940:
0941: if (hasStringValue()) {
0942: return 1;
0943: }
0944: ChildNode node = (ChildNode) value;
0945: int length = 0;
0946: for (; node != null; node = node.nextSibling) {
0947: length++;
0948: }
0949: return length;
0950:
0951: } // getLength():int
0952:
0953: /**
0954: * NodeList method: Return the Nth immediate child of this node, or
0955: * null if the index is out of bounds.
0956: * @return org.w3c.dom.Node
0957: * @param Index int
0958: */
0959: public Node item(int index) {
0960:
0961: if (hasStringValue()) {
0962: if (index != 0 || value == null) {
0963: return null;
0964: } else {
0965: makeChildNode();
0966: return (Node) value;
0967: }
0968: }
0969: ChildNode node = (ChildNode) value;
0970: for (int i = 0; i < index && node != null; i++) {
0971: node = node.nextSibling;
0972: }
0973: return node;
0974:
0975: } // item(int):Node
0976:
0977: //
0978: // DOM2: methods, getters, setters
0979: //
0980:
0981: //
0982: // Public methods
0983: //
0984:
0985: /**
0986: * Override default behavior so that if deep is true, children are also
0987: * toggled.
0988: * @see Node
0989: * <P>
0990: * Note: this will not change the state of an EntityReference or its
0991: * children, which are always read-only.
0992: */
0993: public void setReadOnly(boolean readOnly, boolean deep) {
0994:
0995: super .setReadOnly(readOnly, deep);
0996:
0997: if (deep) {
0998:
0999: if (needsSyncChildren()) {
1000: synchronizeChildren();
1001: }
1002:
1003: if (hasStringValue()) {
1004: return;
1005: }
1006: // Recursively set kids
1007: for (ChildNode mykid = (ChildNode) value; mykid != null; mykid = mykid.nextSibling) {
1008: if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1009: mykid.setReadOnly(readOnly, true);
1010: }
1011: }
1012: }
1013: } // setReadOnly(boolean,boolean)
1014:
1015: //
1016: // Protected methods
1017: //
1018:
1019: /**
1020: * Override this method in subclass to hook in efficient
1021: * internal data structure.
1022: */
1023: protected void synchronizeChildren() {
1024: // By default just change the flag to avoid calling this method again
1025: needsSyncChildren(false);
1026: }
1027:
1028: /**
1029: * Checks the normalized state of this node after inserting a child.
1030: * If the inserted child causes this node to be unnormalized, then this
1031: * node is flagged accordingly.
1032: * The conditions for changing the normalized state are:
1033: * <ul>
1034: * <li>The inserted child is a text node and one of its adjacent siblings
1035: * is also a text node.
1036: * <li>The inserted child is is itself unnormalized.
1037: * </ul>
1038: *
1039: * @param insertedChild the child node that was inserted into this node
1040: *
1041: * @throws NullPointerException if the inserted child is <code>null</code>
1042: */
1043: void checkNormalizationAfterInsert(ChildNode insertedChild) {
1044: // See if insertion caused this node to be unnormalized.
1045: if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1046: ChildNode prev = insertedChild.previousSibling();
1047: ChildNode next = insertedChild.nextSibling;
1048: // If an adjacent sibling of the new child is a text node,
1049: // flag this node as unnormalized.
1050: if ((prev != null && prev.getNodeType() == Node.TEXT_NODE)
1051: || (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1052: isNormalized(false);
1053: }
1054: } else {
1055: // If the new child is not normalized,
1056: // then this node is inherently not normalized.
1057: if (!insertedChild.isNormalized()) {
1058: isNormalized(false);
1059: }
1060: }
1061: } // checkNormalizationAfterInsert(ChildNode)
1062:
1063: /**
1064: * Checks the normalized of this node after removing a child.
1065: * If the removed child causes this node to be unnormalized, then this
1066: * node is flagged accordingly.
1067: * The conditions for changing the normalized state are:
1068: * <ul>
1069: * <li>The removed child had two adjacent siblings that were text nodes.
1070: * </ul>
1071: *
1072: * @param previousSibling the previous sibling of the removed child, or
1073: * <code>null</code>
1074: */
1075: void checkNormalizationAfterRemove(ChildNode previousSibling) {
1076: // See if removal caused this node to be unnormalized.
1077: // If the adjacent siblings of the removed child were both text nodes,
1078: // flag this node as unnormalized.
1079: if (previousSibling != null
1080: && previousSibling.getNodeType() == Node.TEXT_NODE) {
1081:
1082: ChildNode next = previousSibling.nextSibling;
1083: if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1084: isNormalized(false);
1085: }
1086: }
1087: } // checkNormalizationAfterRemove(ChildNode)
1088:
1089: //
1090: // Serialization methods
1091: //
1092:
1093: /** Serialize object. */
1094: private void writeObject(ObjectOutputStream out) throws IOException {
1095:
1096: // synchronize chilren
1097: if (needsSyncChildren()) {
1098: synchronizeChildren();
1099: }
1100: // write object
1101: out.defaultWriteObject();
1102:
1103: } // writeObject(ObjectOutputStream)
1104:
1105: /** Deserialize object. */
1106: private void readObject(ObjectInputStream ois)
1107: throws ClassNotFoundException, IOException {
1108:
1109: // perform default deseralization
1110: ois.defaultReadObject();
1111:
1112: // hardset synchildren - so we don't try to sync -
1113: // it does not make any sense to try to synchildren when we just
1114: // deserialize object.
1115: needsSyncChildren(false);
1116:
1117: } // readObject(ObjectInputStream)
1118:
1119: } // class AttrImpl
|