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