Save This Page
Home » openjdk-7 » com.sun.org.apache.xerces.internal » dom » [javadoc | source]
    1   /*
    2    * reserved comment block
    3    * DO NOT REMOVE OR ALTER!
    4    */
    5   /*
    6    * Copyright 1999-2004 The Apache Software Foundation.
    7    *
    8    * Licensed under the Apache License, Version 2.0 (the "License");
    9    * you may not use this file except in compliance with the License.
   10    * You may obtain a copy of the License at
   11    *
   12    *      http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing, software
   15    * distributed under the License is distributed on an "AS IS" BASIS,
   16    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   17    * See the License for the specific language governing permissions and
   18    * limitations under the License.
   19    */
   20   
   21   package com.sun.org.apache.xerces.internal.dom;
   22   
   23   import java.io.IOException;
   24   import java.io.ObjectInputStream;
   25   import java.io.ObjectOutputStream;
   26   
   27   import org.w3c.dom.TypeInfo;
   28   import org.w3c.dom.Attr;
   29   import org.w3c.dom.DOMException;
   30   import org.w3c.dom.Element;
   31   import org.w3c.dom.Node;
   32   import org.w3c.dom.NodeList;
   33   import org.w3c.dom.Text;
   34   
   35   /**
   36    * Attribute represents an XML-style attribute of an
   37    * Element. Typically, the allowable values are controlled by its
   38    * declaration in the Document Type Definition (DTD) governing this
   39    * kind of document.
   40    * <P>
   41    * If the attribute has not been explicitly assigned a value, but has
   42    * been declared in the DTD, it will exist and have that default. Only
   43    * if neither the document nor the DTD specifies a value will the
   44    * Attribute really be considered absent and have no value; in that
   45    * case, querying the attribute will return null.
   46    * <P>
   47    * Attributes may have multiple children that contain their data. (XML
   48    * allows attributes to contain entity references, and tokenized
   49    * attribute types such as NMTOKENS may have a child for each token.)
   50    * For convenience, the Attribute object's getValue() method returns
   51    * the string version of the attribute's value.
   52    * <P>
   53    * Attributes are not children of the Elements they belong to, in the
   54    * usual sense, and have no valid Parent reference. However, the spec
   55    * says they _do_ belong to a specific Element, and an INUSE exception
   56    * is to be thrown if the user attempts to explicitly share them
   57    * between elements.
   58    * <P>
   59    * Note that Elements do not permit attributes to appear to be shared
   60    * (see the INUSE exception), so this object's mutability is
   61    * officially not an issue.
   62    * <p>
   63    * Note: The ownerNode attribute is used to store the Element the Attr
   64    * node is associated with. Attr nodes do not have parent nodes.
   65    * Besides, the getOwnerElement() method can be used to get the element node
   66    * this attribute is associated with.
   67    * <P>
   68    * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
   69    * it, does.
   70    *
   71    * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
   72    * NodeImpl and provide its own implementation of the ParentNode's behavior.
   73    * The reason is that we now try and avoid to always create a Text node to
   74    * hold the value of an attribute. The DOM spec requires it, so we still have
   75    * to do it in case getFirstChild() is called for instance. The reason
   76    * attribute values are stored as a list of nodes is so that they can carry
   77    * more than a simple string. They can also contain EntityReference nodes.
   78    * However, most of the times people only have a single string that they only
   79    * set and get through Element.set/getAttribute or Attr.set/getValue. In this
   80    * new version, the Attr node has a value pointer which can either be the
   81    * String directly or a pointer to the first ChildNode. A flag tells which one
   82    * it currently is. Note that while we try to stick with the direct String as
   83    * much as possible once we've switched to a node there is no going back. This
   84    * is because we have no way to know whether the application keeps referring to
   85    * the node we once returned.
   86    * <p> The gain in memory varies on the density of attributes in the document.
   87    * But in the tests I've run I've seen up to 12% of memory gain. And the good
   88    * thing is that it also leads to a slight gain in speed because we allocate
   89    * fewer objects! I mean, that's until we have to actually create the node...
   90    * <p>
   91    * To avoid too much duplicated code, I got rid of ParentNode and renamed
   92    * ChildAndParentNode, which I never really liked, to ParentNode for
   93    * simplicity, this doesn't make much of a difference in memory usage because
   94    * there are only very few objects that are only a Parent. This is only true
   95    * now because AttrImpl now inherits directly from NodeImpl and has its own
   96    * implementation of the ParentNode's node behavior. So there is still some
   97    * duplicated code there.
   98    * <p>
   99    * This class doesn't directly support mutation events, however, it notifies
  100    * the document when mutations are performed so that the document class do so.
  101    *
  102    * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  103    * ParentNode, be careful to keep these two classes in sync!
  104    *
  105    * @xerces.internal
  106    *
  107    * @see AttrNSImpl
  108    *
  109    * @author Arnaud  Le Hors, IBM
  110    * @author Joe Kesselman, IBM
  111    * @author Andy Clark, IBM
  112    * @since PR-DOM-Level-1-19980818.
  113    *
  114    */
  115   public class AttrImpl
  116       extends NodeImpl
  117       implements Attr, TypeInfo{
  118   
  119       //
  120       // Constants
  121       //
  122   
  123       /** Serialization version. */
  124       static final long serialVersionUID = 7277707688218972102L;
  125   
  126       /** DTD namespace. **/
  127       static final String DTD_URI = "http://www.w3.org/TR/REC-xml";
  128   
  129       //
  130       // Data
  131       //
  132   
  133       /** This can either be a String or the first child node. */
  134       protected Object value = null;
  135   
  136       /** Attribute name. */
  137       protected String name;
  138   
  139       /** Type information */
  140       // REVISIT: we are losing the type information in DOM during serialization
  141       transient Object type;
  142   
  143       protected static TextImpl textNode = null;
  144   
  145       //
  146       // Constructors
  147       //
  148   
  149       /**
  150        * Attribute has no public constructor. Please use the factory
  151        * method in the Document class.
  152        */
  153       protected AttrImpl(CoreDocumentImpl ownerDocument, String name) {
  154           super(ownerDocument);
  155           this.name = name;
  156           /** False for default attributes. */
  157           isSpecified(true);
  158           hasStringValue(true);
  159       }
  160   
  161       // for AttrNSImpl
  162       protected AttrImpl() {}
  163   
  164       // Support for DOM Level 3 renameNode method.
  165       // Note: This only deals with part of the pb. It is expected to be
  166       // called after the Attr has been detached for one thing.
  167       // CoreDocumentImpl does all the work.
  168       void rename(String name) {
  169           if (needsSyncData()) {
  170               synchronizeData();
  171           }
  172           this.name = name;
  173       }
  174   
  175       // create a real text node as child if we don't have one yet
  176       protected void makeChildNode() {
  177           if (hasStringValue()) {
  178               if (value != null) {
  179                   TextImpl text =
  180                       (TextImpl) ownerDocument().createTextNode((String) value);
  181                   value = text;
  182                   text.isFirstChild(true);
  183                   text.previousSibling = text;
  184                   text.ownerNode = this;
  185                   text.isOwned(true);
  186               }
  187               hasStringValue(false);
  188           }
  189       }
  190   
  191       /**
  192        * NON-DOM
  193        * set the ownerDocument of this node and its children
  194        */
  195       void setOwnerDocument(CoreDocumentImpl doc) {
  196           if (needsSyncChildren()) {
  197               synchronizeChildren();
  198           }
  199           super.setOwnerDocument(doc);
  200           if (!hasStringValue()) {
  201               for (ChildNode child = (ChildNode) value;
  202                    child != null; child = child.nextSibling) {
  203                   child.setOwnerDocument(doc);
  204               }
  205           }
  206       }
  207   
  208       /**
  209        * NON-DOM: set the type of this attribute to be ID type.
  210        *
  211        * @param id
  212        */
  213       public void setIdAttribute(boolean id){
  214           if (needsSyncData()) {
  215               synchronizeData();
  216           }
  217           isIdAttribute(id);
  218       }
  219       /** DOM Level 3: isId*/
  220       public boolean isId(){
  221           // REVISIT: should an attribute that is not in the tree return
  222           // isID true?
  223           return isIdAttribute();
  224       }
  225   
  226   
  227       //
  228       // Node methods
  229       //
  230   
  231       public Node cloneNode(boolean deep) {
  232   
  233           if (needsSyncChildren()) {
  234               synchronizeChildren();
  235           }
  236           AttrImpl clone = (AttrImpl) super.cloneNode(deep);
  237   
  238           // take care of case where there are kids
  239           if (!clone.hasStringValue()) {
  240   
  241               // Need to break the association w/ original kids
  242               clone.value = null;
  243   
  244               // Cloning an Attribute always clones its children,
  245               // since they represent its value, no matter whether this
  246               // is a deep clone or not
  247               for (Node child = (Node) value; child != null;
  248                    child = child.getNextSibling()) {
  249                    clone.appendChild(child.cloneNode(true));
  250               }
  251           }
  252           clone.isSpecified(true);
  253           return clone;
  254       }
  255   
  256       /**
  257        * A short integer indicating what type of node this is. The named
  258        * constants for this value are defined in the org.w3c.dom.Node interface.
  259        */
  260       public short getNodeType() {
  261           return Node.ATTRIBUTE_NODE;
  262       }
  263   
  264       /**
  265        * Returns the attribute name
  266        */
  267       public String getNodeName() {
  268           if (needsSyncData()) {
  269               synchronizeData();
  270           }
  271           return name;
  272       }
  273   
  274       /**
  275        * Implicit in the rerouting of getNodeValue to getValue is the
  276        * need to redefine setNodeValue, for symmetry's sake.  Note that
  277        * since we're explicitly providing a value, Specified should be set
  278        * true.... even if that value equals the default.
  279        */
  280       public void setNodeValue(String value) throws DOMException {
  281           setValue(value);
  282       }
  283   
  284       /**
  285        * @see org.w3c.dom.TypeInfo#getTypeName()
  286        */
  287       public String getTypeName() {
  288           return (String)type;
  289       }
  290   
  291       /**
  292        * @see org.w3c.dom.TypeInfo#getTypeNamespace()
  293        */
  294       public String getTypeNamespace() {
  295           if (type != null) {
  296               return DTD_URI;
  297           }
  298           return null;
  299       }
  300   
  301       /**
  302        * Method getSchemaTypeInfo.
  303        * @return TypeInfo
  304        */
  305       public TypeInfo getSchemaTypeInfo(){
  306         return this;
  307       }
  308   
  309       /**
  310        * In Attribute objects, NodeValue is considered a synonym for
  311        * Value.
  312        *
  313        * @see #getValue()
  314        */
  315       public String getNodeValue() {
  316           return getValue();
  317       }
  318   
  319       //
  320       // Attr methods
  321       //
  322   
  323       /**
  324        * In Attributes, NodeName is considered a synonym for the
  325        * attribute's Name
  326        */
  327       public String getName() {
  328   
  329           if (needsSyncData()) {
  330               synchronizeData();
  331           }
  332           return name;
  333   
  334       } // getName():String
  335   
  336       /**
  337        * The DOM doesn't clearly define what setValue(null) means. I've taken it
  338        * as "remove all children", which from outside should appear
  339        * similar to setting it to the empty string.
  340        */
  341       public void setValue(String newvalue) {
  342   
  343           CoreDocumentImpl ownerDocument = ownerDocument();
  344   
  345           if (ownerDocument.errorChecking && isReadOnly()) {
  346               String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  347               throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  348           }
  349   
  350           Element ownerElement = getOwnerElement();
  351           String oldvalue = "";
  352           if (needsSyncData()) {
  353               synchronizeData();
  354           }
  355           if (needsSyncChildren()) {
  356               synchronizeChildren();
  357           }
  358           if (value != null) {
  359               if (ownerDocument.getMutationEvents()) {
  360                   // Can no longer just discard the kids; they may have
  361                   // event listeners waiting for them to disconnect.
  362                   if (hasStringValue()) {
  363                       oldvalue = (String) value;
  364                       // create an actual text node as our child so
  365                       // that we can use it in the event
  366                       if (textNode == null) {
  367                           textNode = (TextImpl)
  368                               ownerDocument.createTextNode((String) value);
  369                       }
  370                       else {
  371                           textNode.data = (String) value;
  372                       }
  373                       value = textNode;
  374                       textNode.isFirstChild(true);
  375                       textNode.previousSibling = textNode;
  376                       textNode.ownerNode = this;
  377                       textNode.isOwned(true);
  378                       hasStringValue(false);
  379                       internalRemoveChild(textNode, true);
  380                   }
  381                   else {
  382                       oldvalue = getValue();
  383                       while (value != null) {
  384                           internalRemoveChild((Node) value, true);
  385                       }
  386                   }
  387               }
  388               else {
  389                   if (hasStringValue()) {
  390                       oldvalue = (String) value;
  391                   }
  392                   else {
  393                       // simply discard children if any
  394                       oldvalue = getValue();
  395                       // remove ref from first child to last child
  396                       ChildNode firstChild = (ChildNode) value;
  397                       firstChild.previousSibling = null;
  398                       firstChild.isFirstChild(false);
  399                       firstChild.ownerNode = ownerDocument;
  400                   }
  401                   // then remove ref to current value
  402                   value = null;
  403                   needsSyncChildren(false);
  404               }
  405               if (isIdAttribute() && ownerElement != null) {
  406                   ownerDocument.removeIdentifier(oldvalue);
  407               }
  408           }
  409   
  410           // Create and add the new one, generating only non-aggregate events
  411           // (There are no listeners on the new Text, but there may be
  412           // capture/bubble listeners on the Attr.
  413           // Note that aggregate events are NOT dispatched here,
  414           // since we need to combine the remove and insert.
  415           isSpecified(true);
  416           if (ownerDocument.getMutationEvents()) {
  417               // if there are any event handlers create a real node
  418               internalInsertBefore(ownerDocument.createTextNode(newvalue),
  419                                    null, true);
  420               hasStringValue(false);
  421               // notify document
  422               ownerDocument.modifiedAttrValue(this, oldvalue);
  423           } else {
  424               // directly store the string
  425               value = newvalue;
  426               hasStringValue(true);
  427               changed();
  428           }
  429           if (isIdAttribute() && ownerElement != null) {
  430               ownerDocument.putIdentifier(newvalue, ownerElement);
  431           }
  432   
  433       } // setValue(String)
  434   
  435       /**
  436        * The "string value" of an Attribute is its text representation,
  437        * which in turn is a concatenation of the string values of its children.
  438        */
  439       public String getValue() {
  440   
  441           if (needsSyncData()) {
  442               synchronizeData();
  443           }
  444           if (needsSyncChildren()) {
  445               synchronizeChildren();
  446           }
  447           if (value == null) {
  448               return "";
  449           }
  450           if (hasStringValue()) {
  451               return (String) value;
  452           }
  453   
  454           ChildNode firstChild = ((ChildNode) value);
  455   
  456           String data = null;
  457           if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){
  458                   data = ((EntityReferenceImpl)firstChild).getEntityRefValue();
  459           }
  460           else {
  461                   data =  firstChild.getNodeValue();
  462           }
  463   
  464           ChildNode node = firstChild.nextSibling;
  465   
  466           if (node == null || data == null)  return (data == null)?"":data;
  467   
  468           StringBuffer value = new StringBuffer(data);
  469           while (node != null) {
  470               if (node.getNodeType()  == Node.ENTITY_REFERENCE_NODE){
  471                   data = ((EntityReferenceImpl)node).getEntityRefValue();
  472                   if (data == null) return "";
  473                   value.append(data);
  474               }
  475               else {
  476                   value.append(node.getNodeValue());
  477               }
  478               node = node.nextSibling;
  479           }
  480           return value.toString();
  481   
  482       } // getValue():String
  483   
  484   
  485       /**
  486        * The "specified" flag is true if and only if this attribute's
  487        * value was explicitly specified in the original document. Note that
  488        * the implementation, not the user, is in charge of this
  489        * property. If the user asserts an Attribute value (even if it ends
  490        * up having the same value as the default), it is considered a
  491        * specified attribute. If you really want to revert to the default,
  492        * delete the attribute from the Element, and the Implementation will
  493        * re-assert the default (if any) in its place, with the appropriate
  494        * specified=false setting.
  495        */
  496       public boolean getSpecified() {
  497   
  498           if (needsSyncData()) {
  499               synchronizeData();
  500           }
  501           return isSpecified();
  502   
  503       } // getSpecified():boolean
  504   
  505       //
  506       // Attr2 methods
  507       //
  508   
  509       /**
  510        * Returns the element node that this attribute is associated with,
  511        * or null if the attribute has not been added to an element.
  512        *
  513        * @see #getOwnerElement
  514        *
  515        * @deprecated Previous working draft of DOM Level 2. New method
  516        *             is <tt>getOwnerElement()</tt>.
  517        */
  518       public Element getElement() {
  519           // if we have an owner, ownerNode is our ownerElement, otherwise it's
  520           // our ownerDocument and we don't have an ownerElement
  521           return (Element) (isOwned() ? ownerNode : null);
  522       }
  523   
  524       /**
  525        * Returns the element node that this attribute is associated with,
  526        * or null if the attribute has not been added to an element.
  527        *
  528        * @since WD-DOM-Level-2-19990719
  529        */
  530       public Element getOwnerElement() {
  531           // if we have an owner, ownerNode is our ownerElement, otherwise it's
  532           // our ownerDocument and we don't have an ownerElement
  533           return (Element) (isOwned() ? ownerNode : null);
  534       }
  535   
  536       public void normalize() {
  537   
  538           // No need to normalize if already normalized or
  539           // if value is kept as a String.
  540           if (isNormalized() || hasStringValue())
  541               return;
  542   
  543           Node kid, next;
  544           ChildNode firstChild = (ChildNode)value;
  545           for (kid = firstChild; kid != null; kid = next) {
  546               next = kid.getNextSibling();
  547   
  548               // If kid is a text node, we need to check for one of two
  549               // conditions:
  550               //   1) There is an adjacent text node
  551               //   2) There is no adjacent text node, but kid is
  552               //      an empty text node.
  553               if ( kid.getNodeType() == Node.TEXT_NODE )
  554               {
  555                   // If an adjacent text node, merge it with kid
  556                   if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
  557                   {
  558                       ((Text)kid).appendData(next.getNodeValue());
  559                       removeChild( next );
  560                       next = kid; // Don't advance; there might be another.
  561                   }
  562                   else
  563                   {
  564                       // If kid is empty, remove it
  565                       if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
  566                           removeChild( kid );
  567                       }
  568                   }
  569               }
  570           }
  571   
  572           isNormalized(true);
  573       } // normalize()
  574   
  575       //
  576       // Public methods
  577       //
  578   
  579       /** NON-DOM, for use by parser */
  580       public void setSpecified(boolean arg) {
  581   
  582           if (needsSyncData()) {
  583               synchronizeData();
  584           }
  585           isSpecified(arg);
  586   
  587       } // setSpecified(boolean)
  588   
  589           /**
  590            * NON-DOM: used by the parser
  591            * @param type
  592            */
  593       public void setType (Object type){
  594           this.type = type;
  595       }
  596   
  597       //
  598       // Object methods
  599       //
  600   
  601       /** NON-DOM method for debugging convenience */
  602       public String toString() {
  603           return getName() + "=" + "\"" + getValue() + "\"";
  604       }
  605   
  606       /**
  607        * Test whether this node has any children. Convenience shorthand
  608        * for (Node.getFirstChild()!=null)
  609        */
  610       public boolean hasChildNodes() {
  611           if (needsSyncChildren()) {
  612               synchronizeChildren();
  613           }
  614           return value != null;
  615       }
  616   
  617       /**
  618        * Obtain a NodeList enumerating all children of this node. If there
  619        * are none, an (initially) empty NodeList is returned.
  620        * <p>
  621        * NodeLists are "live"; as children are added/removed the NodeList
  622        * will immediately reflect those changes. Also, the NodeList refers
  623        * to the actual nodes, so changes to those nodes made via the DOM tree
  624        * will be reflected in the NodeList and vice versa.
  625        * <p>
  626        * In this implementation, Nodes implement the NodeList interface and
  627        * provide their own getChildNodes() support. Other DOMs may solve this
  628        * differently.
  629        */
  630       public NodeList getChildNodes() {
  631           // JKESS: KNOWN ISSUE HERE
  632   
  633           if (needsSyncChildren()) {
  634               synchronizeChildren();
  635           }
  636           return this;
  637   
  638       } // getChildNodes():NodeList
  639   
  640       /** The first child of this Node, or null if none. */
  641       public Node getFirstChild() {
  642   
  643           if (needsSyncChildren()) {
  644               synchronizeChildren();
  645           }
  646           makeChildNode();
  647           return (Node) value;
  648   
  649       }   // getFirstChild():Node
  650   
  651       /** The last child of this Node, or null if none. */
  652       public Node getLastChild() {
  653   
  654           if (needsSyncChildren()) {
  655               synchronizeChildren();
  656           }
  657           return lastChild();
  658   
  659       } // getLastChild():Node
  660   
  661       final ChildNode lastChild() {
  662           // last child is stored as the previous sibling of first child
  663           makeChildNode();
  664           return value != null ? ((ChildNode) value).previousSibling : null;
  665       }
  666   
  667       final void lastChild(ChildNode node) {
  668           // store lastChild as previous sibling of first child
  669           if (value != null) {
  670               ((ChildNode) value).previousSibling = node;
  671           }
  672       }
  673   
  674       /**
  675        * Move one or more node(s) to our list of children. Note that this
  676        * implicitly removes them from their previous parent.
  677        *
  678        * @param newChild The Node to be moved to our subtree. As a
  679        * convenience feature, inserting a DocumentNode will instead insert
  680        * all its children.
  681        *
  682        * @param refChild Current child which newChild should be placed
  683        * immediately before. If refChild is null, the insertion occurs
  684        * after all existing Nodes, like appendChild().
  685        *
  686        * @return newChild, in its new state (relocated, or emptied in the case of
  687        * DocumentNode.)
  688        *
  689        * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  690        * type that shouldn't be a child of this node, or if newChild is an
  691        * ancestor of this node.
  692        *
  693        * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  694        * different owner document than we do.
  695        *
  696        * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
  697        * this node.
  698        *
  699        * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  700        * read-only.
  701        */
  702       public Node insertBefore(Node newChild, Node refChild)
  703           throws DOMException {
  704           // Tail-call; optimizer should be able to do good things with.
  705           return internalInsertBefore(newChild, refChild, false);
  706       } // insertBefore(Node,Node):Node
  707   
  708       /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  709        * to control which mutation events are spawned. This version of the
  710        * insertBefore operation allows us to do so. It is not intended
  711        * for use by application programs.
  712        */
  713       Node internalInsertBefore(Node newChild, Node refChild, boolean replace)
  714           throws DOMException {
  715   
  716           CoreDocumentImpl ownerDocument = ownerDocument();
  717           boolean errorChecking = ownerDocument.errorChecking;
  718   
  719           if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
  720               // SLOW BUT SAFE: We could insert the whole subtree without
  721               // juggling so many next/previous pointers. (Wipe out the
  722               // parent's child-list, patch the parent pointers, set the
  723               // ends of the list.) But we know some subclasses have special-
  724               // case behavior they add to insertBefore(), so we don't risk it.
  725               // This approch also takes fewer bytecodes.
  726   
  727               // NOTE: If one of the children is not a legal child of this
  728               // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
  729               // have been transferred. (Alternative behaviors would be to
  730               // reparent up to the first failure point or reparent all those
  731               // which are acceptable to the target node, neither of which is
  732               // as robust. PR-DOM-0818 isn't entirely clear on which it
  733               // recommends?????
  734   
  735               // No need to check kids for right-document; if they weren't,
  736               // they wouldn't be kids of that DocFrag.
  737               if (errorChecking) {
  738                   for (Node kid = newChild.getFirstChild(); // Prescan
  739                        kid != null; kid = kid.getNextSibling()) {
  740   
  741                       if (!ownerDocument.isKidOK(this, kid)) {
  742                           String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  743                           throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  744                       }
  745                   }
  746               }
  747   
  748               while (newChild.hasChildNodes()) {
  749                   insertBefore(newChild.getFirstChild(), refChild);
  750               }
  751               return newChild;
  752           }
  753   
  754           if (newChild == refChild) {
  755               // stupid case that must be handled as a no-op triggering events...
  756               refChild = refChild.getNextSibling();
  757               removeChild(newChild);
  758               insertBefore(newChild, refChild);
  759               return newChild;
  760           }
  761   
  762           if (needsSyncChildren()) {
  763               synchronizeChildren();
  764           }
  765   
  766           if (errorChecking) {
  767               if (isReadOnly()) {
  768                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  769                   throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  770               }
  771               if (newChild.getOwnerDocument() != ownerDocument) {
  772                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
  773                   throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
  774               }
  775               if (!ownerDocument.isKidOK(this, newChild)) {
  776                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  777                   throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  778               }
  779               // refChild must be a child of this node (or null)
  780               if (refChild != null && refChild.getParentNode() != this) {
  781                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  782                   throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  783               }
  784   
  785               // Prevent cycles in the tree
  786               // newChild cannot be ancestor of this Node,
  787               // and actually cannot be this
  788               boolean treeSafe = true;
  789               for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
  790               {
  791                   treeSafe = newChild != a;
  792               }
  793               if (!treeSafe) {
  794                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
  795                   throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
  796               }
  797           }
  798   
  799           makeChildNode(); // make sure we have a node and not a string
  800   
  801           // notify document
  802           ownerDocument.insertingNode(this, replace);
  803   
  804           // Convert to internal type, to avoid repeated casting
  805           ChildNode newInternal = (ChildNode)newChild;
  806   
  807           Node oldparent = newInternal.parentNode();
  808           if (oldparent != null) {
  809               oldparent.removeChild(newInternal);
  810           }
  811   
  812           // Convert to internal type, to avoid repeated casting
  813           ChildNode refInternal = (ChildNode) refChild;
  814   
  815           // Attach up
  816           newInternal.ownerNode = this;
  817           newInternal.isOwned(true);
  818   
  819           // Attach before and after
  820           // Note: firstChild.previousSibling == lastChild!!
  821           ChildNode firstChild = (ChildNode) value;
  822           if (firstChild == null) {
  823               // this our first and only child
  824               value = newInternal; // firstchild = newInternal;
  825               newInternal.isFirstChild(true);
  826               newInternal.previousSibling = newInternal;
  827           }
  828           else {
  829               if (refInternal == null) {
  830                   // this is an append
  831                   ChildNode lastChild = firstChild.previousSibling;
  832                   lastChild.nextSibling = newInternal;
  833                   newInternal.previousSibling = lastChild;
  834                   firstChild.previousSibling = newInternal;
  835               }
  836               else {
  837                   // this is an insert
  838                   if (refChild == firstChild) {
  839                       // at the head of the list
  840                       firstChild.isFirstChild(false);
  841                       newInternal.nextSibling = firstChild;
  842                       newInternal.previousSibling = firstChild.previousSibling;
  843                       firstChild.previousSibling = newInternal;
  844                       value = newInternal; // firstChild = newInternal;
  845                       newInternal.isFirstChild(true);
  846                   }
  847                   else {
  848                       // somewhere in the middle
  849                       ChildNode prev = refInternal.previousSibling;
  850                       newInternal.nextSibling = refInternal;
  851                       prev.nextSibling = newInternal;
  852                       refInternal.previousSibling = newInternal;
  853                       newInternal.previousSibling = prev;
  854                   }
  855               }
  856           }
  857   
  858           changed();
  859   
  860           // notify document
  861           ownerDocument.insertedNode(this, newInternal, replace);
  862   
  863           checkNormalizationAfterInsert(newInternal);
  864   
  865           return newChild;
  866   
  867       } // internalInsertBefore(Node,Node,int):Node
  868   
  869       /**
  870        * Remove a child from this Node. The removed child's subtree
  871        * remains intact so it may be re-inserted elsewhere.
  872        *
  873        * @return oldChild, in its new state (removed).
  874        *
  875        * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  876        * this node.
  877        *
  878        * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  879        * read-only.
  880        */
  881       public Node removeChild(Node oldChild)
  882           throws DOMException {
  883           // Tail-call, should be optimizable
  884           if (hasStringValue()) {
  885               // we don't have any child per say so it can't be one of them!
  886               String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  887               throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  888           }
  889           return internalRemoveChild(oldChild, false);
  890       } // removeChild(Node) :Node
  891   
  892       /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  893        * to control which mutation events are spawned. This version of the
  894        * removeChild operation allows us to do so. It is not intended
  895        * for use by application programs.
  896        */
  897       Node internalRemoveChild(Node oldChild, boolean replace)
  898           throws DOMException {
  899   
  900           CoreDocumentImpl ownerDocument = ownerDocument();
  901           if (ownerDocument.errorChecking) {
  902               if (isReadOnly()) {
  903                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
  904                   throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
  905               }
  906               if (oldChild != null && oldChild.getParentNode() != this) {
  907                   String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
  908                   throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
  909               }
  910           }
  911   
  912           ChildNode oldInternal = (ChildNode) oldChild;
  913   
  914           // notify document
  915           ownerDocument.removingNode(this, oldInternal, replace);
  916   
  917           // Patch linked list around oldChild
  918           // Note: lastChild == firstChild.previousSibling
  919           if (oldInternal == value) { // oldInternal == firstChild
  920               // removing first child
  921               oldInternal.isFirstChild(false);
  922               // next line is: firstChild = oldInternal.nextSibling
  923               value = oldInternal.nextSibling;
  924               ChildNode firstChild = (ChildNode) value;
  925               if (firstChild != null) {
  926                   firstChild.isFirstChild(true);
  927                   firstChild.previousSibling = oldInternal.previousSibling;
  928               }
  929           } else {
  930               ChildNode prev = oldInternal.previousSibling;
  931               ChildNode next = oldInternal.nextSibling;
  932               prev.nextSibling = next;
  933               if (next == null) {
  934                   // removing last child
  935                   ChildNode firstChild = (ChildNode) value;
  936                   firstChild.previousSibling = prev;
  937               } else {
  938                   // removing some other child in the middle
  939                   next.previousSibling = prev;
  940               }
  941           }
  942   
  943           // Save previous sibling for normalization checking.
  944           ChildNode oldPreviousSibling = oldInternal.previousSibling();
  945   
  946           // Remove oldInternal's references to tree
  947           oldInternal.ownerNode       = ownerDocument;
  948           oldInternal.isOwned(false);
  949           oldInternal.nextSibling     = null;
  950           oldInternal.previousSibling = null;
  951   
  952           changed();
  953   
  954           // notify document
  955           ownerDocument.removedNode(this, replace);
  956   
  957           checkNormalizationAfterRemove(oldPreviousSibling);
  958   
  959           return oldInternal;
  960   
  961       } // internalRemoveChild(Node,int):Node
  962   
  963       /**
  964        * Make newChild occupy the location that oldChild used to
  965        * have. Note that newChild will first be removed from its previous
  966        * parent, if any. Equivalent to inserting newChild before oldChild,
  967        * then removing oldChild.
  968        *
  969        * @return oldChild, in its new state (removed).
  970        *
  971        * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  972        * type that shouldn't be a child of this node, or if newChild is
  973        * one of our ancestors.
  974        *
  975        * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  976        * different owner document than we do.
  977        *
  978        * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  979        * this node.
  980        *
  981        * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  982        * read-only.
  983        */
  984       public Node replaceChild(Node newChild, Node oldChild)
  985           throws DOMException {
  986   
  987           makeChildNode();
  988   
  989           // If Mutation Events are being generated, this operation might
  990           // throw aggregate events twice when modifying an Attr -- once
  991           // on insertion and once on removal. DOM Level 2 does not specify
  992           // this as either desirable or undesirable, but hints that
  993           // aggregations should be issued only once per user request.
  994   
  995           // notify document
  996           CoreDocumentImpl ownerDocument = ownerDocument();
  997           ownerDocument.replacingNode(this);
  998   
  999           internalInsertBefore(newChild, oldChild, true);
 1000           if (newChild != oldChild) {
 1001               internalRemoveChild(oldChild, true);
 1002           }
 1003   
 1004           // notify document
 1005           ownerDocument.replacedNode(this);
 1006   
 1007           return oldChild;
 1008       }
 1009   
 1010       //
 1011       // NodeList methods
 1012       //
 1013   
 1014       /**
 1015        * NodeList method: Count the immediate children of this node
 1016        * @return int
 1017        */
 1018       public int getLength() {
 1019   
 1020           if (hasStringValue()) {
 1021               return 1;
 1022           }
 1023           ChildNode node = (ChildNode) value;
 1024           int length = 0;
 1025           for (; node != null; node = node.nextSibling) {
 1026               length++;
 1027           }
 1028           return length;
 1029   
 1030       } // getLength():int
 1031   
 1032       /**
 1033        * NodeList method: Return the Nth immediate child of this node, or
 1034        * null if the index is out of bounds.
 1035        * @return org.w3c.dom.Node
 1036        * @param Index int
 1037        */
 1038       public Node item(int index) {
 1039   
 1040           if (hasStringValue()) {
 1041               if (index != 0 || value == null) {
 1042                   return null;
 1043               }
 1044               else {
 1045                   makeChildNode();
 1046                   return (Node) value;
 1047               }
 1048           }
 1049           if (index < 0) {
 1050               return null;
 1051           }
 1052           ChildNode node = (ChildNode) value;
 1053           for (int i = 0; i < index && node != null; i++) {
 1054               node = node.nextSibling;
 1055           }
 1056           return node;
 1057   
 1058       } // item(int):Node
 1059   
 1060       //
 1061       // DOM3
 1062       //
 1063   
 1064       /**
 1065        * DOM Level 3 WD- Experimental.
 1066        * Override inherited behavior from ParentNode to support deep equal.
 1067        * isEqualNode is always deep on Attr nodes.
 1068        */
 1069       public boolean isEqualNode(Node arg) {
 1070           return super.isEqualNode(arg);
 1071       }
 1072   
 1073       /**
 1074        * Introduced in DOM Level 3. <p>
 1075        * Checks if a type is derived from another by restriction. See:
 1076        * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
 1077        *
 1078        * @param ancestorNS
 1079        *        The namspace of the ancestor type declaration
 1080        * @param ancestorName
 1081        *        The name of the ancestor type declaration
 1082        * @param type
 1083        *        The reference type definition
 1084        *
 1085        * @return boolean True if the type is derived by restriciton for the
 1086        *         reference type
 1087        */
 1088       public boolean isDerivedFrom(String typeNamespaceArg,
 1089                                    String typeNameArg,
 1090                                    int derivationMethod) {
 1091   
 1092           return false;
 1093       }
 1094   
 1095   
 1096       //
 1097       // Public methods
 1098       //
 1099   
 1100       /**
 1101        * Override default behavior so that if deep is true, children are also
 1102        * toggled.
 1103        * @see Node
 1104        * <P>
 1105        * Note: this will not change the state of an EntityReference or its
 1106        * children, which are always read-only.
 1107        */
 1108       public void setReadOnly(boolean readOnly, boolean deep) {
 1109   
 1110           super.setReadOnly(readOnly, deep);
 1111   
 1112           if (deep) {
 1113   
 1114               if (needsSyncChildren()) {
 1115                   synchronizeChildren();
 1116               }
 1117   
 1118               if (hasStringValue()) {
 1119                   return;
 1120               }
 1121               // Recursively set kids
 1122               for (ChildNode mykid = (ChildNode) value;
 1123                    mykid != null;
 1124                    mykid = mykid.nextSibling) {
 1125                   if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
 1126                       mykid.setReadOnly(readOnly,true);
 1127                   }
 1128               }
 1129           }
 1130       } // setReadOnly(boolean,boolean)
 1131   
 1132       //
 1133       // Protected methods
 1134       //
 1135   
 1136       /**
 1137        * Override this method in subclass to hook in efficient
 1138        * internal data structure.
 1139        */
 1140       protected void synchronizeChildren() {
 1141           // By default just change the flag to avoid calling this method again
 1142           needsSyncChildren(false);
 1143       }
 1144   
 1145       /**
 1146        * Checks the normalized state of this node after inserting a child.
 1147        * If the inserted child causes this node to be unnormalized, then this
 1148        * node is flagged accordingly.
 1149        * The conditions for changing the normalized state are:
 1150        * <ul>
 1151        * <li>The inserted child is a text node and one of its adjacent siblings
 1152        * is also a text node.
 1153        * <li>The inserted child is is itself unnormalized.
 1154        * </ul>
 1155        *
 1156        * @param insertedChild the child node that was inserted into this node
 1157        *
 1158        * @throws NullPointerException if the inserted child is <code>null</code>
 1159        */
 1160       void checkNormalizationAfterInsert(ChildNode insertedChild) {
 1161           // See if insertion caused this node to be unnormalized.
 1162           if (insertedChild.getNodeType() == Node.TEXT_NODE) {
 1163               ChildNode prev = insertedChild.previousSibling();
 1164               ChildNode next = insertedChild.nextSibling;
 1165               // If an adjacent sibling of the new child is a text node,
 1166               // flag this node as unnormalized.
 1167               if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
 1168                   (next != null && next.getNodeType() == Node.TEXT_NODE)) {
 1169                   isNormalized(false);
 1170               }
 1171           }
 1172           else {
 1173               // If the new child is not normalized,
 1174               // then this node is inherently not normalized.
 1175               if (!insertedChild.isNormalized()) {
 1176                   isNormalized(false);
 1177               }
 1178           }
 1179       } // checkNormalizationAfterInsert(ChildNode)
 1180   
 1181       /**
 1182        * Checks the normalized of this node after removing a child.
 1183        * If the removed child causes this node to be unnormalized, then this
 1184        * node is flagged accordingly.
 1185        * The conditions for changing the normalized state are:
 1186        * <ul>
 1187        * <li>The removed child had two adjacent siblings that were text nodes.
 1188        * </ul>
 1189        *
 1190        * @param previousSibling the previous sibling of the removed child, or
 1191        * <code>null</code>
 1192        */
 1193       void checkNormalizationAfterRemove(ChildNode previousSibling) {
 1194           // See if removal caused this node to be unnormalized.
 1195           // If the adjacent siblings of the removed child were both text nodes,
 1196           // flag this node as unnormalized.
 1197           if (previousSibling != null &&
 1198               previousSibling.getNodeType() == Node.TEXT_NODE) {
 1199   
 1200               ChildNode next = previousSibling.nextSibling;
 1201               if (next != null && next.getNodeType() == Node.TEXT_NODE) {
 1202                   isNormalized(false);
 1203               }
 1204           }
 1205       } // checkNormalizationAfterRemove(ChildNode)
 1206   
 1207       //
 1208       // Serialization methods
 1209       //
 1210   
 1211       /** Serialize object. */
 1212       private void writeObject(ObjectOutputStream out) throws IOException {
 1213   
 1214           // synchronize chilren
 1215           if (needsSyncChildren()) {
 1216               synchronizeChildren();
 1217           }
 1218           // write object
 1219           out.defaultWriteObject();
 1220   
 1221       } // writeObject(ObjectOutputStream)
 1222   
 1223       /** Deserialize object. */
 1224       private void readObject(ObjectInputStream ois)
 1225           throws ClassNotFoundException, IOException {
 1226   
 1227           // perform default deseralization
 1228           ois.defaultReadObject();
 1229   
 1230           // hardset synchildren - so we don't try to sync -
 1231           // it does not make any sense to try to synchildren when we just
 1232           // deserialize object.
 1233           needsSyncChildren(false);
 1234   
 1235       } // readObject(ObjectInputStream)
 1236   
 1237   
 1238   } // class AttrImpl

Save This Page
Home » openjdk-7 » com.sun.org.apache.xerces.internal » dom » [javadoc | source]