KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > dom > AttrImpl


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.dom;
18
19 import java.io.IOException JavaDoc;
20 import java.io.ObjectInputStream JavaDoc;
21 import java.io.ObjectOutputStream JavaDoc;
22
23 import org.w3c.dom.TypeInfo JavaDoc;
24 import org.w3c.dom.Attr JavaDoc;
25 import org.w3c.dom.DOMException JavaDoc;
26 import org.w3c.dom.Element JavaDoc;
27 import org.w3c.dom.Node JavaDoc;
28 import org.w3c.dom.NodeList JavaDoc;
29 import org.w3c.dom.Text JavaDoc;
30
31 /**
32  * Attribute represents an XML-style attribute of an
33  * Element. Typically, the allowable values are controlled by its
34  * declaration in the Document Type Definition (DTD) governing this
35  * kind of document.
36  * <P>
37  * If the attribute has not been explicitly assigned a value, but has
38  * been declared in the DTD, it will exist and have that default. Only
39  * if neither the document nor the DTD specifies a value will the
40  * Attribute really be considered absent and have no value; in that
41  * case, querying the attribute will return null.
42  * <P>
43  * Attributes may have multiple children that contain their data. (XML
44  * allows attributes to contain entity references, and tokenized
45  * attribute types such as NMTOKENS may have a child for each token.)
46  * For convenience, the Attribute object's getValue() method returns
47  * the string version of the attribute's value.
48  * <P>
49  * Attributes are not children of the Elements they belong to, in the
50  * usual sense, and have no valid Parent reference. However, the spec
51  * says they _do_ belong to a specific Element, and an INUSE exception
52  * is to be thrown if the user attempts to explicitly share them
53  * between elements.
54  * <P>
55  * Note that Elements do not permit attributes to appear to be shared
56  * (see the INUSE exception), so this object's mutability is
57  * officially not an issue.
58  * <p>
59  * Note: The ownerNode attribute is used to store the Element the Attr
60  * node is associated with. Attr nodes do not have parent nodes.
61  * Besides, the getOwnerElement() method can be used to get the element node
62  * this attribute is associated with.
63  * <P>
64  * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
65  * it, does.
66  *
67  * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
68  * NodeImpl and provide its own implementation of the ParentNode's behavior.
69  * The reason is that we now try and avoid to always create a Text node to
70  * hold the value of an attribute. The DOM spec requires it, so we still have
71  * to do it in case getFirstChild() is called for instance. The reason
72  * attribute values are stored as a list of nodes is so that they can carry
73  * more than a simple string. They can also contain EntityReference nodes.
74  * However, most of the times people only have a single string that they only
75  * set and get through Element.set/getAttribute or Attr.set/getValue. In this
76  * new version, the Attr node has a value pointer which can either be the
77  * String directly or a pointer to the first ChildNode. A flag tells which one
78  * it currently is. Note that while we try to stick with the direct String as
79  * much as possible once we've switched to a node there is no going back. This
80  * is because we have no way to know whether the application keeps referring to
81  * the node we once returned.
82  * <p> The gain in memory varies on the density of attributes in the document.
83  * But in the tests I've run I've seen up to 12% of memory gain. And the good
84  * thing is that it also leads to a slight gain in speed because we allocate
85  * fewer objects! I mean, that's until we have to actually create the node...
86  * <p>
87  * To avoid too much duplicated code, I got rid of ParentNode and renamed
88  * ChildAndParentNode, which I never really liked, to ParentNode for
89  * simplicity, this doesn't make much of a difference in memory usage because
90  * there are only very few objects that are only a Parent. This is only true
91  * now because AttrImpl now inherits directly from NodeImpl and has its own
92  * implementation of the ParentNode's node behavior. So there is still some
93  * duplicated code there.
94  * <p>
95  * This class doesn't directly support mutation events, however, it notifies
96  * the document when mutations are performed so that the document class do so.
97  *
98  * <p><b>WARNING</b>: Some of the code here is partially duplicated in
99  * ParentNode, be careful to keep these two classes in sync!
100  *
101  * @xerces.internal
102  *
103  * @see AttrNSImpl
104  *
105  * @author Arnaud Le Hors, IBM
106  * @author Joe Kesselman, IBM
107  * @author Andy Clark, IBM
108  * @version $Id: AttrImpl.java,v 1.64 2005/05/02 22:02:22 mrglavas Exp $
109  * @since PR-DOM-Level-1-19980818.
110  *
111  */

112 public class AttrImpl
113     extends NodeImpl
114     implements Attr JavaDoc, TypeInfo JavaDoc{
115
116     //
117
// Constants
118
//
119

120     /** Serialization version. */
121     static final long serialVersionUID = 7277707688218972102L;
122     
123     /** DTD namespace. **/
124     static final String JavaDoc DTD_URI = "http://www.w3.org/TR/REC-xml";
125
126     //
127
// Data
128
//
129

130     /** This can either be a String or the first child node. */
131     protected Object JavaDoc value = null;
132
133     /** Attribute name. */
134     protected String JavaDoc name;
135     
136     /** Type information */
137     // REVISIT: we are losing the type information in DOM during serialization
138
transient Object JavaDoc type;
139
140     protected static TextImpl textNode = null;
141
142     //
143
// Constructors
144
//
145

146     /**
147      * Attribute has no public constructor. Please use the factory
148      * method in the Document class.
149      */

150     protected AttrImpl(CoreDocumentImpl ownerDocument, String JavaDoc name) {
151         super(ownerDocument);
152         this.name = name;
153         /** False for default attributes. */
154         isSpecified(true);
155         hasStringValue(true);
156     }
157
158     // for AttrNSImpl
159
protected AttrImpl() {}
160
161     // Support for DOM Level 3 renameNode method.
162
// Note: This only deals with part of the pb. It is expected to be
163
// called after the Attr has been detached for one thing.
164
// CoreDocumentImpl does all the work.
165
void rename(String JavaDoc name) {
166         if (needsSyncData()) {
167             synchronizeData();
168         }
169         this.name = name;
170     }
171
172     // create a real text node as child if we don't have one yet
173
protected void makeChildNode() {
174         if (hasStringValue()) {
175             if (value != null) {
176                 TextImpl text =
177                     (TextImpl) ownerDocument().createTextNode((String JavaDoc) value);
178                 value = text;
179                 text.isFirstChild(true);
180                 text.previousSibling = text;
181                 text.ownerNode = this;
182                 text.isOwned(true);
183             }
184             hasStringValue(false);
185         }
186     }
187
188     /**
189      * NON-DOM
190      * set the ownerDocument of this node and its children
191      */

192     void setOwnerDocument(CoreDocumentImpl doc) {
193         if (needsSyncChildren()) {
194             synchronizeChildren();
195         }
196         super.setOwnerDocument(doc);
197         if (!hasStringValue()) {
198             for (ChildNode child = (ChildNode) value;
199                  child != null; child = child.nextSibling) {
200                 child.setOwnerDocument(doc);
201             }
202         }
203     }
204
205     /**
206      * NON-DOM: set the type of this attribute to be ID type.
207      *
208      * @param id
209      */

210     public void setIdAttribute(boolean id){
211         if (needsSyncData()) {
212             synchronizeData();
213         }
214         isIdAttribute(id);
215     }
216     /** DOM Level 3: isId*/
217     public boolean isId(){
218         // REVISIT: should an attribute that is not in the tree return
219
// isID true?
220
return isIdAttribute();
221     }
222
223
224     //
225
// Node methods
226
//
227

228     public Node JavaDoc cloneNode(boolean deep) {
229
230         if (needsSyncChildren()) {
231             synchronizeChildren();
232         }
233         AttrImpl clone = (AttrImpl) super.cloneNode(deep);
234
235         // take care of case where there are kids
236
if (!clone.hasStringValue()) {
237
238             // Need to break the association w/ original kids
239
clone.value = null;
240
241             // Cloning an Attribute always clones its children,
242
// since they represent its value, no matter whether this
243
// is a deep clone or not
244
for (Node JavaDoc child = (Node JavaDoc) value; child != null;
245                  child = child.getNextSibling()) {
246                  clone.appendChild(child.cloneNode(true));
247             }
248         }
249         clone.isSpecified(true);
250         return clone;
251     }
252
253     /**
254      * A short integer indicating what type of node this is. The named
255      * constants for this value are defined in the org.w3c.dom.Node interface.
256      */

257     public short getNodeType() {
258         return Node.ATTRIBUTE_NODE;
259     }
260
261     /**
262      * Returns the attribute name
263      */

264     public String JavaDoc getNodeName() {
265         if (needsSyncData()) {
266             synchronizeData();
267         }
268         return name;
269     }
270
271     /**
272      * Implicit in the rerouting of getNodeValue to getValue is the
273      * need to redefine setNodeValue, for symmetry's sake. Note that
274      * since we're explicitly providing a value, Specified should be set
275      * true.... even if that value equals the default.
276      */

277     public void setNodeValue(String JavaDoc value) throws DOMException JavaDoc {
278         setValue(value);
279     }
280     
281     /**
282      * @see org.w3c.dom.TypeInfo#getTypeName()
283      */

284     public String JavaDoc getTypeName() {
285         return (String JavaDoc)type;
286     }
287
288     /**
289      * @see org.w3c.dom.TypeInfo#getTypeNamespace()
290      */

291     public String JavaDoc getTypeNamespace() {
292         if (type != null) {
293             return DTD_URI;
294         }
295         return null;
296     }
297     
298     /**
299      * Method getSchemaTypeInfo.
300      * @return TypeInfo
301      */

302     public TypeInfo JavaDoc getSchemaTypeInfo(){
303       return this;
304     }
305
306     /**
307      * In Attribute objects, NodeValue is considered a synonym for
308      * Value.
309      *
310      * @see #getValue()
311      */

312     public String JavaDoc getNodeValue() {
313         return getValue();
314     }
315
316     //
317
// Attr methods
318
//
319

320     /**
321      * In Attributes, NodeName is considered a synonym for the
322      * attribute's Name
323      */

324     public String JavaDoc getName() {
325
326         if (needsSyncData()) {
327             synchronizeData();
328         }
329         return name;
330
331     } // getName():String
332

333     /**
334      * The DOM doesn't clearly define what setValue(null) means. I've taken it
335      * as "remove all children", which from outside should appear
336      * similar to setting it to the empty string.
337      */

338     public void setValue(String JavaDoc newvalue) {
339
340         CoreDocumentImpl ownerDocument = ownerDocument();
341         
342         if (ownerDocument.errorChecking && isReadOnly()) {
343             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
344             throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
345         }
346         
347         Element JavaDoc ownerElement = getOwnerElement();
348         String JavaDoc oldvalue = "";
349         if (needsSyncData()) {
350             synchronizeData();
351         }
352         if (needsSyncChildren()) {
353             synchronizeChildren();
354         }
355         if (value != null) {
356             if (ownerDocument.getMutationEvents()) {
357                 // Can no longer just discard the kids; they may have
358
// event listeners waiting for them to disconnect.
359
if (hasStringValue()) {
360                     oldvalue = (String JavaDoc) value;
361                     // create an actual text node as our child so
362
// that we can use it in the event
363
if (textNode == null) {
364                         textNode = (TextImpl)
365                             ownerDocument.createTextNode((String JavaDoc) value);
366                     }
367                     else {
368                         textNode.data = (String JavaDoc) value;
369                     }
370                     value = textNode;
371                     textNode.isFirstChild(true);
372                     textNode.previousSibling = textNode;
373                     textNode.ownerNode = this;
374                     textNode.isOwned(true);
375                     hasStringValue(false);
376                     internalRemoveChild(textNode, true);
377                 }
378                 else {
379                     oldvalue = getValue();
380                     while (value != null) {
381                         internalRemoveChild((Node JavaDoc) value, true);
382                     }
383                 }
384             }
385             else {
386                 if (hasStringValue()) {
387                     oldvalue = (String JavaDoc) value;
388                 }
389                 else {
390                     // simply discard children if any
391
oldvalue = getValue();
392                     // remove ref from first child to last child
393
ChildNode firstChild = (ChildNode) value;
394                     firstChild.previousSibling = null;
395                     firstChild.isFirstChild(false);
396                     firstChild.ownerNode = ownerDocument;
397                 }
398                 // then remove ref to current value
399
value = null;
400                 needsSyncChildren(false);
401             }
402             if (isIdAttribute() && ownerElement != null) {
403                 ownerDocument.removeIdentifier(oldvalue);
404             }
405         }
406
407         // Create and add the new one, generating only non-aggregate events
408
// (There are no listeners on the new Text, but there may be
409
// capture/bubble listeners on the Attr.
410
// Note that aggregate events are NOT dispatched here,
411
// since we need to combine the remove and insert.
412
isSpecified(true);
413         if (ownerDocument.getMutationEvents()) {
414             // if there are any event handlers create a real node
415
internalInsertBefore(ownerDocument.createTextNode(newvalue),
416                                  null, true);
417             hasStringValue(false);
418             // notify document
419
ownerDocument.modifiedAttrValue(this, oldvalue);
420         } else {
421             // directly store the string
422
value = newvalue;
423             hasStringValue(true);
424             changed();
425         }
426         if (isIdAttribute() && ownerElement != null) {
427             ownerDocument.putIdentifier(newvalue, ownerElement);
428         }
429
430     } // setValue(String)
431

432     /**
433      * The "string value" of an Attribute is its text representation,
434      * which in turn is a concatenation of the string values of its children.
435      */

436     public String JavaDoc getValue() {
437
438         if (needsSyncData()) {
439             synchronizeData();
440         }
441         if (needsSyncChildren()) {
442             synchronizeChildren();
443         }
444         if (value == null) {
445             return "";
446         }
447         if (hasStringValue()) {
448             return (String JavaDoc) value;
449         }
450         
451         ChildNode firstChild = ((ChildNode) value);
452
453         String JavaDoc data = null;
454         if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){
455                 data = ((EntityReferenceImpl)firstChild).getEntityRefValue();
456         }
457         else {
458                 data = firstChild.getNodeValue();
459         }
460         
461         ChildNode node = firstChild.nextSibling;
462         
463         if (node == null || data == null) return (data == null)?"":data;
464         
465         StringBuffer JavaDoc value = new StringBuffer JavaDoc(data);
466         while (node != null) {
467             if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE){
468                 data = ((EntityReferenceImpl)node).getEntityRefValue();
469                 if (data == null) return "";
470                 value.append(data);
471             }
472             else {
473                 value.append(node.getNodeValue());
474             }
475             node = node.nextSibling;
476         }
477         return value.toString();
478
479     } // getValue():String
480

481     
482     /**
483      * The "specified" flag is true if and only if this attribute's
484      * value was explicitly specified in the original document. Note that
485      * the implementation, not the user, is in charge of this
486      * property. If the user asserts an Attribute value (even if it ends
487      * up having the same value as the default), it is considered a
488      * specified attribute. If you really want to revert to the default,
489      * delete the attribute from the Element, and the Implementation will
490      * re-assert the default (if any) in its place, with the appropriate
491      * specified=false setting.
492      */

493     public boolean getSpecified() {
494
495         if (needsSyncData()) {
496             synchronizeData();
497         }
498         return isSpecified();
499
500     } // getSpecified():boolean
501

502     //
503
// Attr2 methods
504
//
505

506     /**
507      * Returns the element node that this attribute is associated with,
508      * or null if the attribute has not been added to an element.
509      *
510      * @see #getOwnerElement
511      *
512      * @deprecated Previous working draft of DOM Level 2. New method
513      * is <tt>getOwnerElement()</tt>.
514      */

515     public Element JavaDoc getElement() {
516         // if we have an owner, ownerNode is our ownerElement, otherwise it's
517
// our ownerDocument and we don't have an ownerElement
518
return (Element JavaDoc) (isOwned() ? ownerNode : null);
519     }
520
521     /**
522      * Returns the element node that this attribute is associated with,
523      * or null if the attribute has not been added to an element.
524      *
525      * @since WD-DOM-Level-2-19990719
526      */

527     public Element JavaDoc getOwnerElement() {
528         // if we have an owner, ownerNode is our ownerElement, otherwise it's
529
// our ownerDocument and we don't have an ownerElement
530
return (Element JavaDoc) (isOwned() ? ownerNode : null);
531     }
532     
533     public void normalize() {
534
535         // No need to normalize if already normalized or
536
// if value is kept as a String.
537
if (isNormalized() || hasStringValue())
538             return;
539
540         Node JavaDoc kid, next;
541         ChildNode firstChild = (ChildNode)value;
542         for (kid = firstChild; kid != null; kid = next) {
543             next = kid.getNextSibling();
544
545             // If kid is a text node, we need to check for one of two
546
// conditions:
547
// 1) There is an adjacent text node
548
// 2) There is no adjacent text node, but kid is
549
// an empty text node.
550
if ( kid.getNodeType() == Node.TEXT_NODE )
551             {
552                 // If an adjacent text node, merge it with kid
553
if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
554                 {
555                     ((Text JavaDoc)kid).appendData(next.getNodeValue());
556                     removeChild( next );
557                     next = kid; // Don't advance; there might be another.
558
}
559                 else
560                 {
561                     // If kid is empty, remove it
562
if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
563                         removeChild( kid );
564                     }
565                 }
566             }
567         }
568
569         isNormalized(true);
570     } // normalize()
571

572     //
573
// Public methods
574
//
575

576     /** NON-DOM, for use by parser */
577     public void setSpecified(boolean arg) {
578
579         if (needsSyncData()) {
580             synchronizeData();
581         }
582         isSpecified(arg);
583
584     } // setSpecified(boolean)
585

586     /**
587      * NON-DOM: used by the parser
588      * @param type
589      */

590     public void setType (Object JavaDoc type){
591         this.type = type;
592     }
593
594     //
595
// Object methods
596
//
597

598     /** NON-DOM method for debugging convenience */
599     public String JavaDoc toString() {
600         return getName() + "=" + "\"" + getValue() + "\"";
601     }
602
603     /**
604      * Test whether this node has any children. Convenience shorthand
605      * for (Node.getFirstChild()!=null)
606      */

607     public boolean hasChildNodes() {
608         if (needsSyncChildren()) {
609             synchronizeChildren();
610         }
611         return value != null;
612     }
613
614     /**
615      * Obtain a NodeList enumerating all children of this node. If there
616      * are none, an (initially) empty NodeList is returned.
617      * <p>
618      * NodeLists are "live"; as children are added/removed the NodeList
619      * will immediately reflect those changes. Also, the NodeList refers
620      * to the actual nodes, so changes to those nodes made via the DOM tree
621      * will be reflected in the NodeList and vice versa.
622      * <p>
623      * In this implementation, Nodes implement the NodeList interface and
624      * provide their own getChildNodes() support. Other DOMs may solve this
625      * differently.
626      */

627     public NodeList JavaDoc getChildNodes() {
628         // JKESS: KNOWN ISSUE HERE
629

630         if (needsSyncChildren()) {
631             synchronizeChildren();
632         }
633         return this;
634
635     } // getChildNodes():NodeList
636

637     /** The first child of this Node, or null if none. */
638     public Node JavaDoc getFirstChild() {
639
640         if (needsSyncChildren()) {
641             synchronizeChildren();
642         }
643         makeChildNode();
644         return (Node JavaDoc) value;
645
646     } // getFirstChild():Node
647

648     /** The last child of this Node, or null if none. */
649     public Node JavaDoc getLastChild() {
650
651         if (needsSyncChildren()) {
652             synchronizeChildren();
653         }
654         return lastChild();
655
656     } // getLastChild():Node
657

658     final ChildNode lastChild() {
659         // last child is stored as the previous sibling of first child
660
makeChildNode();
661         return value != null ? ((ChildNode) value).previousSibling : null;
662     }
663
664     final void lastChild(ChildNode node) {
665         // store lastChild as previous sibling of first child
666
if (value != null) {
667             ((ChildNode) value).previousSibling = node;
668         }
669     }
670
671     /**
672      * Move one or more node(s) to our list of children. Note that this
673      * implicitly removes them from their previous parent.
674      *
675      * @param newChild The Node to be moved to our subtree. As a
676      * convenience feature, inserting a DocumentNode will instead insert
677      * all its children.
678      *
679      * @param refChild Current child which newChild should be placed
680      * immediately before. If refChild is null, the insertion occurs
681      * after all existing Nodes, like appendChild().
682      *
683      * @return newChild, in its new state (relocated, or emptied in the case of
684      * DocumentNode.)
685      *
686      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
687      * type that shouldn't be a child of this node, or if newChild is an
688      * ancestor of this node.
689      *
690      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
691      * different owner document than we do.
692      *
693      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
694      * this node.
695      *
696      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
697      * read-only.
698      */

699     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
700         throws DOMException JavaDoc {
701         // Tail-call; optimizer should be able to do good things with.
702
return internalInsertBefore(newChild, refChild, false);
703     } // insertBefore(Node,Node):Node
704

705     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
706      * to control which mutation events are spawned. This version of the
707      * insertBefore operation allows us to do so. It is not intended
708      * for use by application programs.
709      */

710     Node JavaDoc internalInsertBefore(Node JavaDoc newChild, Node JavaDoc refChild, boolean replace)
711         throws DOMException JavaDoc {
712
713         CoreDocumentImpl ownerDocument = ownerDocument();
714         boolean errorChecking = ownerDocument.errorChecking;
715
716         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
717             // SLOW BUT SAFE: We could insert the whole subtree without
718
// juggling so many next/previous pointers. (Wipe out the
719
// parent's child-list, patch the parent pointers, set the
720
// ends of the list.) But we know some subclasses have special-
721
// case behavior they add to insertBefore(), so we don't risk it.
722
// This approch also takes fewer bytecodes.
723

724             // NOTE: If one of the children is not a legal child of this
725
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
726
// have been transferred. (Alternative behaviors would be to
727
// reparent up to the first failure point or reparent all those
728
// which are acceptable to the target node, neither of which is
729
// as robust. PR-DOM-0818 isn't entirely clear on which it
730
// recommends?????
731

732             // No need to check kids for right-document; if they weren't,
733
// they wouldn't be kids of that DocFrag.
734
if (errorChecking) {
735                 for (Node JavaDoc kid = newChild.getFirstChild(); // Prescan
736
kid != null; kid = kid.getNextSibling()) {
737
738                     if (!ownerDocument.isKidOK(this, kid)) {
739                         String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
740                         throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
741                     }
742                 }
743             }
744
745             while (newChild.hasChildNodes()) {
746                 insertBefore(newChild.getFirstChild(), refChild);
747             }
748             return newChild;
749         }
750
751         if (newChild == refChild) {
752             // stupid case that must be handled as a no-op triggering events...
753
refChild = refChild.getNextSibling();
754             removeChild(newChild);
755             insertBefore(newChild, refChild);
756             return newChild;
757         }
758
759         if (needsSyncChildren()) {
760             synchronizeChildren();
761         }
762
763         if (errorChecking) {
764             if (isReadOnly()) {
765                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
766                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
767             }
768             if (newChild.getOwnerDocument() != ownerDocument) {
769                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
770                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR, msg);
771             }
772             if (!ownerDocument.isKidOK(this, newChild)) {
773                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
774                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
775             }
776             // refChild must be a child of this node (or null)
777
if (refChild != null && refChild.getParentNode() != this) {
778                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
779                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
780             }
781
782             // Prevent cycles in the tree
783
// newChild cannot be ancestor of this Node,
784
// and actually cannot be this
785
boolean treeSafe = true;
786             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
787             {
788                 treeSafe = newChild != a;
789             }
790             if (!treeSafe) {
791                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
792                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
793             }
794         }
795
796         makeChildNode(); // make sure we have a node and not a string
797

798         // notify document
799
ownerDocument.insertingNode(this, replace);
800
801         // Convert to internal type, to avoid repeated casting
802
ChildNode newInternal = (ChildNode)newChild;
803
804         Node JavaDoc oldparent = newInternal.parentNode();
805         if (oldparent != null) {
806             oldparent.removeChild(newInternal);
807         }
808
809         // Convert to internal type, to avoid repeated casting
810
ChildNode refInternal = (ChildNode) refChild;
811
812         // Attach up
813
newInternal.ownerNode = this;
814         newInternal.isOwned(true);
815
816         // Attach before and after
817
// Note: firstChild.previousSibling == lastChild!!
818
ChildNode firstChild = (ChildNode) value;
819         if (firstChild == null) {
820             // this our first and only child
821
value = newInternal; // firstchild = newInternal;
822
newInternal.isFirstChild(true);
823             newInternal.previousSibling = newInternal;
824         }
825         else {
826             if (refInternal == null) {
827                 // this is an append
828
ChildNode lastChild = firstChild.previousSibling;
829                 lastChild.nextSibling = newInternal;
830                 newInternal.previousSibling = lastChild;
831                 firstChild.previousSibling = newInternal;
832             }
833             else {
834                 // this is an insert
835
if (refChild == firstChild) {
836                     // at the head of the list
837
firstChild.isFirstChild(false);
838                     newInternal.nextSibling = firstChild;
839                     newInternal.previousSibling = firstChild.previousSibling;
840                     firstChild.previousSibling = newInternal;
841                     value = newInternal; // firstChild = newInternal;
842
newInternal.isFirstChild(true);
843                 }
844                 else {
845                     // somewhere in the middle
846
ChildNode prev = refInternal.previousSibling;
847                     newInternal.nextSibling = refInternal;
848                     prev.nextSibling = newInternal;
849                     refInternal.previousSibling = newInternal;
850                     newInternal.previousSibling = prev;
851                 }
852             }
853         }
854
855         changed();
856
857         // notify document
858
ownerDocument.insertedNode(this, newInternal, replace);
859
860         checkNormalizationAfterInsert(newInternal);
861
862         return newChild;
863
864     } // internalInsertBefore(Node,Node,int):Node
865

866     /**
867      * Remove a child from this Node. The removed child's subtree
868      * remains intact so it may be re-inserted elsewhere.
869      *
870      * @return oldChild, in its new state (removed).
871      *
872      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
873      * this node.
874      *
875      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
876      * read-only.
877      */

878     public Node JavaDoc removeChild(Node JavaDoc oldChild)
879         throws DOMException JavaDoc {
880         // Tail-call, should be optimizable
881
if (hasStringValue()) {
882             // we don't have any child per say so it can't be one of them!
883
String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
884             throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
885         }
886         return internalRemoveChild(oldChild, false);
887     } // removeChild(Node) :Node
888

889     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
890      * to control which mutation events are spawned. This version of the
891      * removeChild operation allows us to do so. It is not intended
892      * for use by application programs.
893      */

894     Node JavaDoc internalRemoveChild(Node JavaDoc oldChild, boolean replace)
895         throws DOMException JavaDoc {
896
897         CoreDocumentImpl ownerDocument = ownerDocument();
898         if (ownerDocument.errorChecking) {
899             if (isReadOnly()) {
900                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
901                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
902             }
903             if (oldChild != null && oldChild.getParentNode() != this) {
904                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
905                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
906             }
907         }
908
909         ChildNode oldInternal = (ChildNode) oldChild;
910
911         // notify document
912
ownerDocument.removingNode(this, oldInternal, replace);
913
914         // Patch linked list around oldChild
915
// Note: lastChild == firstChild.previousSibling
916
if (oldInternal == value) { // oldInternal == firstChild
917
// removing first child
918
oldInternal.isFirstChild(false);
919             // next line is: firstChild = oldInternal.nextSibling
920
value = oldInternal.nextSibling;
921             ChildNode firstChild = (ChildNode) value;
922             if (firstChild != null) {
923                 firstChild.isFirstChild(true);
924                 firstChild.previousSibling = oldInternal.previousSibling;
925             }
926         } else {
927             ChildNode prev = oldInternal.previousSibling;
928             ChildNode next = oldInternal.nextSibling;
929             prev.nextSibling = next;
930             if (next == null) {
931                 // removing last child
932
ChildNode firstChild = (ChildNode) value;
933                 firstChild.previousSibling = prev;
934             } else {
935                 // removing some other child in the middle
936
next.previousSibling = prev;
937             }
938         }
939
940         // Save previous sibling for normalization checking.
941
ChildNode oldPreviousSibling = oldInternal.previousSibling();
942
943         // Remove oldInternal's references to tree
944
oldInternal.ownerNode = ownerDocument;
945         oldInternal.isOwned(false);
946         oldInternal.nextSibling = null;
947         oldInternal.previousSibling = null;
948
949         changed();
950
951         // notify document
952
ownerDocument.removedNode(this, replace);
953
954         checkNormalizationAfterRemove(oldPreviousSibling);
955
956         return oldInternal;
957
958     } // internalRemoveChild(Node,int):Node
959

960     /**
961      * Make newChild occupy the location that oldChild used to
962      * have. Note that newChild will first be removed from its previous
963      * parent, if any. Equivalent to inserting newChild before oldChild,
964      * then removing oldChild.
965      *
966      * @return oldChild, in its new state (removed).
967      *
968      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
969      * type that shouldn't be a child of this node, or if newChild is
970      * one of our ancestors.
971      *
972      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
973      * different owner document than we do.
974      *
975      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
976      * this node.
977      *
978      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
979      * read-only.
980      */

981     public Node JavaDoc replaceChild(Node JavaDoc newChild, Node JavaDoc oldChild)
982         throws DOMException JavaDoc {
983
984         makeChildNode();
985
986         // If Mutation Events are being generated, this operation might
987
// throw aggregate events twice when modifying an Attr -- once
988
// on insertion and once on removal. DOM Level 2 does not specify
989
// this as either desirable or undesirable, but hints that
990
// aggregations should be issued only once per user request.
991

992         // notify document
993
CoreDocumentImpl ownerDocument = ownerDocument();
994         ownerDocument.replacingNode(this);
995
996         internalInsertBefore(newChild, oldChild, true);
997         if (newChild != oldChild) {
998             internalRemoveChild(oldChild, true);
999         }
1000
1001        // notify document
1002
ownerDocument.replacedNode(this);
1003
1004        return oldChild;
1005    }
1006
1007    //
1008
// NodeList methods
1009
//
1010

1011    /**
1012     * NodeList method: Count the immediate children of this node
1013     * @return int
1014     */

1015    public int getLength() {
1016
1017        if (hasStringValue()) {
1018            return 1;
1019        }
1020        ChildNode node = (ChildNode) value;
1021        int length = 0;
1022        for (; node != null; node = node.nextSibling) {
1023            length++;
1024        }
1025        return length;
1026
1027    } // getLength():int
1028

1029    /**
1030     * NodeList method: Return the Nth immediate child of this node, or
1031     * null if the index is out of bounds.
1032     * @return org.w3c.dom.Node
1033     * @param Index int
1034     */

1035    public Node JavaDoc item(int index) {
1036
1037        if (hasStringValue()) {
1038            if (index != 0 || value == null) {
1039                return null;
1040            }
1041            else {
1042                makeChildNode();
1043                return (Node JavaDoc) value;
1044            }
1045        }
1046        if (index < 0) {
1047            return null;
1048        }
1049        ChildNode node = (ChildNode) value;
1050        for (int i = 0; i < index && node != null; i++) {
1051            node = node.nextSibling;
1052        }
1053        return node;
1054
1055    } // item(int):Node
1056

1057    //
1058
// DOM3
1059
//
1060

1061    /**
1062     * DOM Level 3 WD- Experimental.
1063     * Override inherited behavior from ParentNode to support deep equal.
1064     * isEqualNode is always deep on Attr nodes.
1065     */

1066    public boolean isEqualNode(Node JavaDoc arg) {
1067        return super.isEqualNode(arg);
1068    }
1069
1070    /**
1071     * Introduced in DOM Level 3. <p>
1072     * Checks if a type is derived from another by restriction. See:
1073     * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
1074     *
1075     * @param ancestorNS
1076     * The namspace of the ancestor type declaration
1077     * @param ancestorName
1078     * The name of the ancestor type declaration
1079     * @param type
1080     * The reference type definition
1081     *
1082     * @return boolean True if the type is derived by restriciton for the
1083     * reference type
1084     */

1085    public boolean isDerivedFrom(String JavaDoc typeNamespaceArg,
1086                                 String JavaDoc typeNameArg,
1087                                 int derivationMethod) {
1088                                    
1089        return false;
1090    }
1091        
1092
1093    //
1094
// Public methods
1095
//
1096

1097    /**
1098     * Override default behavior so that if deep is true, children are also
1099     * toggled.
1100     * @see Node
1101     * <P>
1102     * Note: this will not change the state of an EntityReference or its
1103     * children, which are always read-only.
1104     */

1105    public void setReadOnly(boolean readOnly, boolean deep) {
1106
1107        super.setReadOnly(readOnly, deep);
1108
1109        if (deep) {
1110
1111            if (needsSyncChildren()) {
1112                synchronizeChildren();
1113            }
1114
1115            if (hasStringValue()) {
1116                return;
1117            }
1118            // Recursively set kids
1119
for (ChildNode mykid = (ChildNode) value;
1120                 mykid != null;
1121                 mykid = mykid.nextSibling) {
1122                if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1123                    mykid.setReadOnly(readOnly,true);
1124                }
1125            }
1126        }
1127    } // setReadOnly(boolean,boolean)
1128

1129    //
1130
// Protected methods
1131
//
1132

1133    /**
1134     * Override this method in subclass to hook in efficient
1135     * internal data structure.
1136     */

1137    protected void synchronizeChildren() {
1138        // By default just change the flag to avoid calling this method again
1139
needsSyncChildren(false);
1140    }
1141
1142    /**
1143     * Checks the normalized state of this node after inserting a child.
1144     * If the inserted child causes this node to be unnormalized, then this
1145     * node is flagged accordingly.
1146     * The conditions for changing the normalized state are:
1147     * <ul>
1148     * <li>The inserted child is a text node and one of its adjacent siblings
1149     * is also a text node.
1150     * <li>The inserted child is is itself unnormalized.
1151     * </ul>
1152     *
1153     * @param insertedChild the child node that was inserted into this node
1154     *
1155     * @throws NullPointerException if the inserted child is <code>null</code>
1156     */

1157    void checkNormalizationAfterInsert(ChildNode insertedChild) {
1158        // See if insertion caused this node to be unnormalized.
1159
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1160            ChildNode prev = insertedChild.previousSibling();
1161            ChildNode next = insertedChild.nextSibling;
1162            // If an adjacent sibling of the new child is a text node,
1163
// flag this node as unnormalized.
1164
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
1165                (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1166                isNormalized(false);
1167            }
1168        }
1169        else {
1170            // If the new child is not normalized,
1171
// then this node is inherently not normalized.
1172
if (!insertedChild.isNormalized()) {
1173                isNormalized(false);
1174            }
1175        }
1176    } // checkNormalizationAfterInsert(ChildNode)
1177

1178    /**
1179     * Checks the normalized of this node after removing a child.
1180     * If the removed child causes this node to be unnormalized, then this
1181     * node is flagged accordingly.
1182     * The conditions for changing the normalized state are:
1183     * <ul>
1184     * <li>The removed child had two adjacent siblings that were text nodes.
1185     * </ul>
1186     *
1187     * @param previousSibling the previous sibling of the removed child, or
1188     * <code>null</code>
1189     */

1190    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1191        // See if removal caused this node to be unnormalized.
1192
// If the adjacent siblings of the removed child were both text nodes,
1193
// flag this node as unnormalized.
1194
if (previousSibling != null &&
1195            previousSibling.getNodeType() == Node.TEXT_NODE) {
1196
1197            ChildNode next = previousSibling.nextSibling;
1198            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1199                isNormalized(false);
1200            }
1201        }
1202    } // checkNormalizationAfterRemove(ChildNode)
1203

1204    //
1205
// Serialization methods
1206
//
1207

1208    /** Serialize object. */
1209    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1210
1211        // synchronize chilren
1212
if (needsSyncChildren()) {
1213            synchronizeChildren();
1214        }
1215        // write object
1216
out.defaultWriteObject();
1217
1218    } // writeObject(ObjectOutputStream)
1219

1220    /** Deserialize object. */
1221    private void readObject(ObjectInputStream JavaDoc ois)
1222        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1223
1224        // perform default deseralization
1225
ois.defaultReadObject();
1226
1227        // hardset synchildren - so we don't try to sync -
1228
// it does not make any sense to try to synchildren when we just
1229
// deserialize object.
1230
needsSyncChildren(false);
1231
1232    } // readObject(ObjectInputStream)
1233

1234
1235} // class AttrImpl
1236
Popular Tags