KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > dom > AttrImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2004 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

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

151 public class AttrImpl
152     extends NodeImpl
153     implements Attr JavaDoc {
154
155     //
156
// Constants
157
//
158

159     /** Serialization version. */
160     static final long serialVersionUID = 7277707688218972102L;
161     
162     /** DTD namespace. **/
163     static final String JavaDoc DTD_URI = "http://www.w3.org/TR/REC-xml";
164
165     //
166
// Data
167
//
168

169     /** This can either be a String or the first child node. */
170     protected Object JavaDoc value = null;
171
172     /** Attribute name. */
173     protected String JavaDoc name;
174     
175     /** Type information */
176     // REVISIT: we are losing the type information in DOM during serialization
177
transient org.w3c.dom.TypeInfo JavaDoc type;
178
179     protected static TextImpl textNode = null;
180
181     //
182
// Constructors
183
//
184

185     /**
186      * Attribute has no public constructor. Please use the factory
187      * method in the Document class.
188      */

189     protected AttrImpl(CoreDocumentImpl ownerDocument, String JavaDoc name) {
190         super(ownerDocument);
191         this.name = name;
192         /** False for default attributes. */
193         isSpecified(true);
194         hasStringValue(true);
195     }
196
197     // for AttrNSImpl
198
protected AttrImpl() {}
199
200     // Support for DOM Level 3 renameNode method.
201
// Note: This only deals with part of the pb. It is expected to be
202
// called after the Attr has been detached for one thing.
203
// CoreDocumentImpl does all the work.
204
void rename(String JavaDoc name) {
205         if (needsSyncData()) {
206             synchronizeData();
207         }
208         this.name = name;
209     }
210
211     // create a real text node as child if we don't have one yet
212
protected void makeChildNode() {
213         if (hasStringValue()) {
214             if (value != null) {
215                 TextImpl text =
216                     (TextImpl) ownerDocument().createTextNode((String JavaDoc) value);
217                 value = text;
218                 text.isFirstChild(true);
219                 text.previousSibling = text;
220                 text.ownerNode = this;
221                 text.isOwned(true);
222             }
223             hasStringValue(false);
224         }
225     }
226
227     /**
228      * NON-DOM
229      * set the ownerDocument of this node and its children
230      */

231     void setOwnerDocument(CoreDocumentImpl doc) {
232         if (needsSyncChildren()) {
233             synchronizeChildren();
234         }
235         super.setOwnerDocument(doc);
236         if (!hasStringValue()) {
237             for (ChildNode child = (ChildNode) value;
238                  child != null; child = child.nextSibling) {
239                 child.setOwnerDocument(doc);
240             }
241         }
242     }
243
244     /**
245      * NON-DOM: set the type of this attribute to be ID type.
246      *
247      * @param id
248      */

249     public void setIdAttribute(boolean id){
250         if (needsSyncData()) {
251             synchronizeData();
252         }
253         isIdAttribute(id);
254     }
255     /** DOM Level 3: isId*/
256     public boolean isId(){
257         // REVISIT: should an attribute that is not in the tree return
258
// isID true?
259
return isIdAttribute();
260     }
261
262
263     //
264
// Node methods
265
//
266

267     public Node JavaDoc cloneNode(boolean deep) {
268
269         if (needsSyncChildren()) {
270             synchronizeChildren();
271         }
272         AttrImpl clone = (AttrImpl) super.cloneNode(deep);
273
274         // take care of case where there are kids
275
if (!clone.hasStringValue()) {
276
277             // Need to break the association w/ original kids
278
clone.value = null;
279
280             // Cloning an Attribute always clones its children,
281
// since they represent its value, no matter whether this
282
// is a deep clone or not
283
for (Node JavaDoc child = (Node JavaDoc) value; child != null;
284                  child = child.getNextSibling()) {
285                  clone.appendChild(child.cloneNode(true));
286             }
287         }
288         clone.isSpecified(true);
289         return clone;
290     }
291
292     /**
293      * A short integer indicating what type of node this is. The named
294      * constants for this value are defined in the org.w3c.dom.Node interface.
295      */

296     public short getNodeType() {
297         return Node.ATTRIBUTE_NODE;
298     }
299
300     /**
301      * Returns the attribute name
302      */

303     public String JavaDoc getNodeName() {
304         if (needsSyncData()) {
305             synchronizeData();
306         }
307         return name;
308     }
309
310     /**
311      * Implicit in the rerouting of getNodeValue to getValue is the
312      * need to redefine setNodeValue, for symmetry's sake. Note that
313      * since we're explicitly providing a value, Specified should be set
314      * true.... even if that value equals the default.
315      */

316     public void setNodeValue(String JavaDoc value) throws DOMException JavaDoc {
317         setValue(value);
318     }
319     
320     /**
321      * @see org.w3c.dom.TypeInfo#getTypeName()
322      */

323     public String JavaDoc getTypeName() {
324          if(type != null)
325             return type.getTypeName();
326          return null;
327     }
328
329     /**
330      * @see org.w3c.dom.TypeInfo#getTypeNamespace()
331      */

332     public String JavaDoc getTypeNamespace() {
333         if (type != null) {
334             return DTD_URI;
335         }
336         return null;
337     }
338     
339     /**
340      * Method getSchemaTypeInfo.
341      * @return TypeInfo
342      */

343     public TypeInfo JavaDoc getSchemaTypeInfo(){
344         if (needsSyncData()) {
345             synchronizeData();
346         }
347         return type;
348     }
349
350     /**
351      * In Attribute objects, NodeValue is considered a synonym for
352      * Value.
353      *
354      * @see #getValue()
355      */

356     public String JavaDoc getNodeValue() {
357         return getValue();
358     }
359
360     //
361
// Attr methods
362
//
363

364     /**
365      * In Attributes, NodeName is considered a synonym for the
366      * attribute's Name
367      */

368     public String JavaDoc getName() {
369
370         if (needsSyncData()) {
371             synchronizeData();
372         }
373         return name;
374
375     } // getName():String
376

377     /**
378      * The DOM doesn't clearly define what setValue(null) means. I've taken it
379      * as "remove all children", which from outside should appear
380      * similar to setting it to the empty string.
381      */

382     public void setValue(String JavaDoc newvalue) {
383
384         if (isReadOnly()) {
385             String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
386             throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
387         }
388         CoreDocumentImpl ownerDocument = ownerDocument();
389         Element JavaDoc ownerElement = getOwnerElement();
390         String JavaDoc oldvalue = "";
391         if (needsSyncData()) {
392             synchronizeData();
393         }
394         if (needsSyncChildren()) {
395             synchronizeChildren();
396         }
397         if (value != null) {
398             if (ownerDocument.getMutationEvents()) {
399                 // Can no longer just discard the kids; they may have
400
// event listeners waiting for them to disconnect.
401
if (hasStringValue()) {
402                     oldvalue = (String JavaDoc) value;
403                     // create an actual text node as our child so
404
// that we can use it in the event
405
if (textNode == null) {
406                         textNode = (TextImpl)
407                             ownerDocument.createTextNode((String JavaDoc) value);
408                     }
409                     else {
410                         textNode.data = (String JavaDoc) value;
411                     }
412                     value = textNode;
413                     textNode.isFirstChild(true);
414                     textNode.previousSibling = textNode;
415                     textNode.ownerNode = this;
416                     textNode.isOwned(true);
417                     hasStringValue(false);
418                     internalRemoveChild(textNode, true);
419                 }
420                 else {
421                     oldvalue = getValue();
422                     while (value != null) {
423                         internalRemoveChild((Node JavaDoc) value, true);
424                     }
425                 }
426             }
427             else {
428                 if (hasStringValue()) {
429                     oldvalue = (String JavaDoc) value;
430                 }
431                 else {
432                     // simply discard children if any
433
oldvalue = getValue();
434                     // remove ref from first child to last child
435
ChildNode firstChild = (ChildNode) value;
436                     firstChild.previousSibling = null;
437                     firstChild.isFirstChild(false);
438                     firstChild.ownerNode = ownerDocument;
439                 }
440                 // then remove ref to current value
441
value = null;
442                 needsSyncChildren(false);
443             }
444             if (isIdAttribute() && ownerElement != null) {
445                 ownerDocument.removeIdentifier(oldvalue);
446             }
447         }
448
449         // Create and add the new one, generating only non-aggregate events
450
// (There are no listeners on the new Text, but there may be
451
// capture/bubble listeners on the Attr.
452
// Note that aggregate events are NOT dispatched here,
453
// since we need to combine the remove and insert.
454
isSpecified(true);
455         if (ownerDocument.getMutationEvents()) {
456             // if there are any event handlers create a real node
457
internalInsertBefore(ownerDocument.createTextNode(newvalue),
458                                  null, true);
459             hasStringValue(false);
460             // notify document
461
ownerDocument.modifiedAttrValue(this, oldvalue);
462         } else {
463             // directly store the string
464
value = newvalue;
465             hasStringValue(true);
466             changed();
467         }
468         if (isIdAttribute() && ownerElement != null) {
469             ownerDocument.putIdentifier(newvalue, ownerElement);
470         }
471
472     } // setValue(String)
473

474     /**
475      * The "string value" of an Attribute is its text representation,
476      * which in turn is a concatenation of the string values of its children.
477      */

478     public String JavaDoc getValue() {
479
480         if (needsSyncData()) {
481             synchronizeData();
482         }
483         if (needsSyncChildren()) {
484             synchronizeChildren();
485         }
486         if (value == null) {
487             return "";
488         }
489         if (hasStringValue()) {
490             return (String JavaDoc) value;
491         }
492         
493         ChildNode firstChild = ((ChildNode) value);
494
495         String JavaDoc data = null;
496         if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){
497                 data = ((EntityReferenceImpl)firstChild).getEntityRefValue();
498         }
499         else {
500                 data = firstChild.getNodeValue();
501         }
502         
503         ChildNode node = firstChild.nextSibling;
504         
505         if (node == null || data == null) return (data == null)?"":data;
506         
507         StringBuffer JavaDoc value = new StringBuffer JavaDoc(data);
508         while (node != null) {
509             if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE){
510                 data = ((EntityReferenceImpl)node).getEntityRefValue();
511                 if (data == null) return "";
512                 value.append(data);
513             }
514             else {
515                 value.append(node.getNodeValue());
516             }
517             node = node.nextSibling;
518         }
519         return value.toString();
520
521     } // getValue():String
522

523     
524     /**
525      * The "specified" flag is true if and only if this attribute's
526      * value was explicitly specified in the original document. Note that
527      * the implementation, not the user, is in charge of this
528      * property. If the user asserts an Attribute value (even if it ends
529      * up having the same value as the default), it is considered a
530      * specified attribute. If you really want to revert to the default,
531      * delete the attribute from the Element, and the Implementation will
532      * re-assert the default (if any) in its place, with the appropriate
533      * specified=false setting.
534      */

535     public boolean getSpecified() {
536
537         if (needsSyncData()) {
538             synchronizeData();
539         }
540         return isSpecified();
541
542     } // getSpecified():boolean
543

544     //
545
// Attr2 methods
546
//
547

548     /**
549      * Returns the element node that this attribute is associated with,
550      * or null if the attribute has not been added to an element.
551      *
552      * @see #getOwnerElement
553      *
554      * @deprecated Previous working draft of DOM Level 2. New method
555      * is <tt>getOwnerElement()</tt>.
556      */

557     public Element JavaDoc getElement() {
558         // if we have an owner, ownerNode is our ownerElement, otherwise it's
559
// our ownerDocument and we don't have an ownerElement
560
return (Element JavaDoc) (isOwned() ? ownerNode : null);
561     }
562
563     /**
564      * Returns the element node that this attribute is associated with,
565      * or null if the attribute has not been added to an element.
566      *
567      * @since WD-DOM-Level-2-19990719
568      */

569     public Element JavaDoc getOwnerElement() {
570         // if we have an owner, ownerNode is our ownerElement, otherwise it's
571
// our ownerDocument and we don't have an ownerElement
572
return (Element JavaDoc) (isOwned() ? ownerNode : null);
573     }
574     
575     public void normalize() {
576
577         // No need to normalize if already normalized or
578
// if value is kept as a String.
579
if (isNormalized() || hasStringValue())
580             return;
581
582         Node JavaDoc kid, next;
583         ChildNode firstChild = (ChildNode)value;
584         for (kid = firstChild; kid != null; kid = next) {
585             next = kid.getNextSibling();
586
587             // If kid is a text node, we need to check for one of two
588
// conditions:
589
// 1) There is an adjacent text node
590
// 2) There is no adjacent text node, but kid is
591
// an empty text node.
592
if ( kid.getNodeType() == Node.TEXT_NODE )
593             {
594                 // If an adjacent text node, merge it with kid
595
if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
596                 {
597                     ((Text JavaDoc)kid).appendData(next.getNodeValue());
598                     removeChild( next );
599                     next = kid; // Don't advance; there might be another.
600
}
601                 else
602                 {
603                     // If kid is empty, remove it
604
if ( kid.getNodeValue().length()==0 )
605                         removeChild( kid );
606                 }
607             }
608         }
609
610         isNormalized(true);
611     } // normalize()
612

613     //
614
// Public methods
615
//
616

617     /** NON-DOM, for use by parser */
618     public void setSpecified(boolean arg) {
619
620         if (needsSyncData()) {
621             synchronizeData();
622         }
623         isSpecified(arg);
624
625     } // setSpecified(boolean)
626

627     /**
628      * NON-DOM: used by the parser
629      * @param type
630      */

631     public void setType (org.w3c.dom.TypeInfo JavaDoc type){
632         this.type = type;
633     }
634
635     //
636
// Object methods
637
//
638

639     /** NON-DOM method for debugging convenience */
640     public String JavaDoc toString() {
641         return getName() + "=" + "\"" + getValue() + "\"";
642     }
643
644     /**
645      * Test whether this node has any children. Convenience shorthand
646      * for (Node.getFirstChild()!=null)
647      */

648     public boolean hasChildNodes() {
649         if (needsSyncChildren()) {
650             synchronizeChildren();
651         }
652         return value != null;
653     }
654
655     /**
656      * Obtain a NodeList enumerating all children of this node. If there
657      * are none, an (initially) empty NodeList is returned.
658      * <p>
659      * NodeLists are "live"; as children are added/removed the NodeList
660      * will immediately reflect those changes. Also, the NodeList refers
661      * to the actual nodes, so changes to those nodes made via the DOM tree
662      * will be reflected in the NodeList and vice versa.
663      * <p>
664      * In this implementation, Nodes implement the NodeList interface and
665      * provide their own getChildNodes() support. Other DOMs may solve this
666      * differently.
667      */

668     public NodeList JavaDoc getChildNodes() {
669         // JKESS: KNOWN ISSUE HERE
670

671         if (needsSyncChildren()) {
672             synchronizeChildren();
673         }
674         return this;
675
676     } // getChildNodes():NodeList
677

678     /** The first child of this Node, or null if none. */
679     public Node JavaDoc getFirstChild() {
680
681         if (needsSyncChildren()) {
682             synchronizeChildren();
683         }
684         makeChildNode();
685         return (Node JavaDoc) value;
686
687     } // getFirstChild():Node
688

689     /** The last child of this Node, or null if none. */
690     public Node JavaDoc getLastChild() {
691
692         if (needsSyncChildren()) {
693             synchronizeChildren();
694         }
695         return lastChild();
696
697     } // getLastChild():Node
698

699     final ChildNode lastChild() {
700         // last child is stored as the previous sibling of first child
701
makeChildNode();
702         return value != null ? ((ChildNode) value).previousSibling : null;
703     }
704
705     final void lastChild(ChildNode node) {
706         // store lastChild as previous sibling of first child
707
if (value != null) {
708             ((ChildNode) value).previousSibling = node;
709         }
710     }
711
712     /**
713      * Move one or more node(s) to our list of children. Note that this
714      * implicitly removes them from their previous parent.
715      *
716      * @param newChild The Node to be moved to our subtree. As a
717      * convenience feature, inserting a DocumentNode will instead insert
718      * all its children.
719      *
720      * @param refChild Current child which newChild should be placed
721      * immediately before. If refChild is null, the insertion occurs
722      * after all existing Nodes, like appendChild().
723      *
724      * @return newChild, in its new state (relocated, or emptied in the case of
725      * DocumentNode.)
726      *
727      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
728      * type that shouldn't be a child of this node, or if newChild is an
729      * ancestor of this node.
730      *
731      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
732      * different owner document than we do.
733      *
734      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
735      * this node.
736      *
737      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
738      * read-only.
739      */

740     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
741         throws DOMException JavaDoc {
742         // Tail-call; optimizer should be able to do good things with.
743
return internalInsertBefore(newChild, refChild, false);
744     } // insertBefore(Node,Node):Node
745

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

751     Node JavaDoc internalInsertBefore(Node JavaDoc newChild, Node JavaDoc refChild, boolean replace)
752         throws DOMException JavaDoc {
753
754         CoreDocumentImpl ownerDocument = ownerDocument();
755         boolean errorChecking = ownerDocument.errorChecking;
756
757         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
758             // SLOW BUT SAFE: We could insert the whole subtree without
759
// juggling so many next/previous pointers. (Wipe out the
760
// parent's child-list, patch the parent pointers, set the
761
// ends of the list.) But we know some subclasses have special-
762
// case behavior they add to insertBefore(), so we don't risk it.
763
// This approch also takes fewer bytecodes.
764

765             // NOTE: If one of the children is not a legal child of this
766
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
767
// have been transferred. (Alternative behaviors would be to
768
// reparent up to the first failure point or reparent all those
769
// which are acceptable to the target node, neither of which is
770
// as robust. PR-DOM-0818 isn't entirely clear on which it
771
// recommends?????
772

773             // No need to check kids for right-document; if they weren't,
774
// they wouldn't be kids of that DocFrag.
775
if (errorChecking) {
776                 for (Node JavaDoc kid = newChild.getFirstChild(); // Prescan
777
kid != null; kid = kid.getNextSibling()) {
778
779                     if (!ownerDocument.isKidOK(this, kid)) {
780                         String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
781                         throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
782                     }
783                 }
784             }
785
786             while (newChild.hasChildNodes()) {
787                 insertBefore(newChild.getFirstChild(), refChild);
788             }
789             return newChild;
790         }
791
792         if (newChild == refChild) {
793             // stupid case that must be handled as a no-op triggering events...
794
refChild = refChild.getNextSibling();
795             removeChild(newChild);
796             insertBefore(newChild, refChild);
797             return newChild;
798         }
799
800         if (needsSyncChildren()) {
801             synchronizeChildren();
802         }
803
804         if (errorChecking) {
805             if (isReadOnly()) {
806                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
807                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
808             }
809             if (newChild.getOwnerDocument() != ownerDocument) {
810                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
811                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR, msg);
812             }
813             if (!ownerDocument.isKidOK(this, newChild)) {
814                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
815                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
816             }
817             // refChild must be a child of this node (or null)
818
if (refChild != null && refChild.getParentNode() != this) {
819                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
820                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
821             }
822
823             // Prevent cycles in the tree
824
// newChild cannot be ancestor of this Node,
825
// and actually cannot be this
826
boolean treeSafe = true;
827             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
828             {
829                 treeSafe = newChild != a;
830             }
831             if (!treeSafe) {
832                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
833                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR, msg);
834             }
835         }
836
837         makeChildNode(); // make sure we have a node and not a string
838

839         // notify document
840
ownerDocument.insertingNode(this, replace);
841
842         // Convert to internal type, to avoid repeated casting
843
ChildNode newInternal = (ChildNode)newChild;
844
845         Node JavaDoc oldparent = newInternal.parentNode();
846         if (oldparent != null) {
847             oldparent.removeChild(newInternal);
848         }
849
850         // Convert to internal type, to avoid repeated casting
851
ChildNode refInternal = (ChildNode) refChild;
852
853         // Attach up
854
newInternal.ownerNode = this;
855         newInternal.isOwned(true);
856
857         // Attach before and after
858
// Note: firstChild.previousSibling == lastChild!!
859
ChildNode firstChild = (ChildNode) value;
860         if (firstChild == null) {
861             // this our first and only child
862
value = newInternal; // firstchild = newInternal;
863
newInternal.isFirstChild(true);
864             newInternal.previousSibling = newInternal;
865         }
866         else {
867             if (refInternal == null) {
868                 // this is an append
869
ChildNode lastChild = firstChild.previousSibling;
870                 lastChild.nextSibling = newInternal;
871                 newInternal.previousSibling = lastChild;
872                 firstChild.previousSibling = newInternal;
873             }
874             else {
875                 // this is an insert
876
if (refChild == firstChild) {
877                     // at the head of the list
878
firstChild.isFirstChild(false);
879                     newInternal.nextSibling = firstChild;
880                     newInternal.previousSibling = firstChild.previousSibling;
881                     firstChild.previousSibling = newInternal;
882                     value = newInternal; // firstChild = newInternal;
883
newInternal.isFirstChild(true);
884                 }
885                 else {
886                     // somewhere in the middle
887
ChildNode prev = refInternal.previousSibling;
888                     newInternal.nextSibling = refInternal;
889                     prev.nextSibling = newInternal;
890                     refInternal.previousSibling = newInternal;
891                     newInternal.previousSibling = prev;
892                 }
893             }
894         }
895
896         changed();
897
898         // notify document
899
ownerDocument.insertedNode(this, newInternal, replace);
900
901         checkNormalizationAfterInsert(newInternal);
902
903         return newChild;
904
905     } // internalInsertBefore(Node,Node,int):Node
906

907     /**
908      * Remove a child from this Node. The removed child's subtree
909      * remains intact so it may be re-inserted elsewhere.
910      *
911      * @return oldChild, in its new state (removed).
912      *
913      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
914      * this node.
915      *
916      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
917      * read-only.
918      */

919     public Node JavaDoc removeChild(Node JavaDoc oldChild)
920         throws DOMException JavaDoc {
921         // Tail-call, should be optimizable
922
if (hasStringValue()) {
923             // we don't have any child per say so it can't be one of them!
924
String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
925             throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
926         }
927         return internalRemoveChild(oldChild, false);
928     } // removeChild(Node) :Node
929

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

935     Node JavaDoc internalRemoveChild(Node JavaDoc oldChild, boolean replace)
936         throws DOMException JavaDoc {
937
938         CoreDocumentImpl ownerDocument = ownerDocument();
939         if (ownerDocument.errorChecking) {
940             if (isReadOnly()) {
941                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
942                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
943             }
944             if (oldChild != null && oldChild.getParentNode() != this) {
945                 String JavaDoc msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
946                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR, msg);
947             }
948         }
949
950         ChildNode oldInternal = (ChildNode) oldChild;
951
952         // notify document
953
ownerDocument.removingNode(this, oldInternal, replace);
954
955         // Patch linked list around oldChild
956
// Note: lastChild == firstChild.previousSibling
957
if (oldInternal == value) { // oldInternal == firstChild
958
// removing first child
959
oldInternal.isFirstChild(false);
960             // next line is: firstChild = oldInternal.nextSibling
961
value = oldInternal.nextSibling;
962             ChildNode firstChild = (ChildNode) value;
963             if (firstChild != null) {
964                 firstChild.isFirstChild(true);
965                 firstChild.previousSibling = oldInternal.previousSibling;
966             }
967         } else {
968             ChildNode prev = oldInternal.previousSibling;
969             ChildNode next = oldInternal.nextSibling;
970             prev.nextSibling = next;
971             if (next == null) {
972                 // removing last child
973
ChildNode firstChild = (ChildNode) value;
974                 firstChild.previousSibling = prev;
975             } else {
976                 // removing some other child in the middle
977
next.previousSibling = prev;
978             }
979         }
980
981         // Save previous sibling for normalization checking.
982
ChildNode oldPreviousSibling = oldInternal.previousSibling();
983
984         // Remove oldInternal's references to tree
985
oldInternal.ownerNode = ownerDocument;
986         oldInternal.isOwned(false);
987         oldInternal.nextSibling = null;
988         oldInternal.previousSibling = null;
989
990         changed();
991
992         // notify document
993
ownerDocument.removedNode(this, replace);
994
995         checkNormalizationAfterRemove(oldPreviousSibling);
996
997         return oldInternal;
998
999     } // internalRemoveChild(Node,int):Node
1000

1001    /**
1002     * Make newChild occupy the location that oldChild used to
1003     * have. Note that newChild will first be removed from its previous
1004     * parent, if any. Equivalent to inserting newChild before oldChild,
1005     * then removing oldChild.
1006     *
1007     * @return oldChild, in its new state (removed).
1008     *
1009     * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
1010     * type that shouldn't be a child of this node, or if newChild is
1011     * one of our ancestors.
1012     *
1013     * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
1014     * different owner document than we do.
1015     *
1016     * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
1017     * this node.
1018     *
1019     * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
1020     * read-only.
1021     */

1022    public Node JavaDoc replaceChild(Node JavaDoc newChild, Node JavaDoc oldChild)
1023        throws DOMException JavaDoc {
1024
1025        makeChildNode();
1026
1027        // If Mutation Events are being generated, this operation might
1028
// throw aggregate events twice when modifying an Attr -- once
1029
// on insertion and once on removal. DOM Level 2 does not specify
1030
// this as either desirable or undesirable, but hints that
1031
// aggregations should be issued only once per user request.
1032

1033        // notify document
1034
CoreDocumentImpl ownerDocument = ownerDocument();
1035        ownerDocument.replacingNode(this);
1036
1037        internalInsertBefore(newChild, oldChild, true);
1038        if (newChild != oldChild) {
1039            internalRemoveChild(oldChild, true);
1040        }
1041
1042        // notify document
1043
ownerDocument.replacedNode(this);
1044
1045        return oldChild;
1046    }
1047
1048    //
1049
// NodeList methods
1050
//
1051

1052    /**
1053     * NodeList method: Count the immediate children of this node
1054     * @return int
1055     */

1056    public int getLength() {
1057
1058        if (hasStringValue()) {
1059            return 1;
1060        }
1061        ChildNode node = (ChildNode) value;
1062        int length = 0;
1063        for (; node != null; node = node.nextSibling) {
1064            length++;
1065        }
1066        return length;
1067
1068    } // getLength():int
1069

1070    /**
1071     * NodeList method: Return the Nth immediate child of this node, or
1072     * null if the index is out of bounds.
1073     * @return org.w3c.dom.Node
1074     * @param Index int
1075     */

1076    public Node JavaDoc item(int index) {
1077
1078        if (hasStringValue()) {
1079            if (index != 0 || value == null) {
1080                return null;
1081            }
1082            else {
1083                makeChildNode();
1084                return (Node JavaDoc) value;
1085            }
1086        }
1087        ChildNode node = (ChildNode) value;
1088        for (int i = 0; i < index && node != null; i++) {
1089            node = node.nextSibling;
1090        }
1091        return node;
1092
1093    } // item(int):Node
1094

1095    //
1096
// DOM3
1097
//
1098

1099    /**
1100     * DOM Level 3 WD- Experimental.
1101     * Override inherited behavior from ParentNode to support deep equal.
1102     * isEqualNode is always deep on Attr nodes.
1103     */

1104    public boolean isEqualNode(Node JavaDoc arg) {
1105        return super.isEqualNode(arg);
1106    }
1107
1108    /**
1109     * DOM Level 3 WD- Experimental.
1110     * @see org.w3c.dom.TypeInfo#isDerivedFrom()
1111     */

1112    public boolean isDerivedFrom(String JavaDoc typeNamespaceArg,
1113                                 String JavaDoc typeNameArg,
1114                                 int derivationMethod) {
1115                                    
1116        return false;
1117    }
1118        
1119
1120    //
1121
// Public methods
1122
//
1123

1124    /**
1125     * Override default behavior so that if deep is true, children are also
1126     * toggled.
1127     * @see Node
1128     * <P>
1129     * Note: this will not change the state of an EntityReference or its
1130     * children, which are always read-only.
1131     */

1132    public void setReadOnly(boolean readOnly, boolean deep) {
1133
1134        super.setReadOnly(readOnly, deep);
1135
1136        if (deep) {
1137
1138            if (needsSyncChildren()) {
1139                synchronizeChildren();
1140            }
1141
1142            if (hasStringValue()) {
1143                return;
1144            }
1145            // Recursively set kids
1146
for (ChildNode mykid = (ChildNode) value;
1147                 mykid != null;
1148                 mykid = mykid.nextSibling) {
1149                if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1150                    mykid.setReadOnly(readOnly,true);
1151                }
1152            }
1153        }
1154    } // setReadOnly(boolean,boolean)
1155

1156    //
1157
// Protected methods
1158
//
1159

1160    /**
1161     * Override this method in subclass to hook in efficient
1162     * internal data structure.
1163     */

1164    protected void synchronizeChildren() {
1165        // By default just change the flag to avoid calling this method again
1166
needsSyncChildren(false);
1167    }
1168
1169    /**
1170     * Checks the normalized state of this node after inserting a child.
1171     * If the inserted child causes this node to be unnormalized, then this
1172     * node is flagged accordingly.
1173     * The conditions for changing the normalized state are:
1174     * <ul>
1175     * <li>The inserted child is a text node and one of its adjacent siblings
1176     * is also a text node.
1177     * <li>The inserted child is is itself unnormalized.
1178     * </ul>
1179     *
1180     * @param insertedChild the child node that was inserted into this node
1181     *
1182     * @throws NullPointerException if the inserted child is <code>null</code>
1183     */

1184    void checkNormalizationAfterInsert(ChildNode insertedChild) {
1185        // See if insertion caused this node to be unnormalized.
1186
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1187            ChildNode prev = insertedChild.previousSibling();
1188            ChildNode next = insertedChild.nextSibling;
1189            // If an adjacent sibling of the new child is a text node,
1190
// flag this node as unnormalized.
1191
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
1192                (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1193                isNormalized(false);
1194            }
1195        }
1196        else {
1197            // If the new child is not normalized,
1198
// then this node is inherently not normalized.
1199
if (!insertedChild.isNormalized()) {
1200                isNormalized(false);
1201            }
1202        }
1203    } // checkNormalizationAfterInsert(ChildNode)
1204

1205    /**
1206     * Checks the normalized of this node after removing a child.
1207     * If the removed child causes this node to be unnormalized, then this
1208     * node is flagged accordingly.
1209     * The conditions for changing the normalized state are:
1210     * <ul>
1211     * <li>The removed child had two adjacent siblings that were text nodes.
1212     * </ul>
1213     *
1214     * @param previousSibling the previous sibling of the removed child, or
1215     * <code>null</code>
1216     */

1217    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1218        // See if removal caused this node to be unnormalized.
1219
// If the adjacent siblings of the removed child were both text nodes,
1220
// flag this node as unnormalized.
1221
if (previousSibling != null &&
1222            previousSibling.getNodeType() == Node.TEXT_NODE) {
1223
1224            ChildNode next = previousSibling.nextSibling;
1225            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1226                isNormalized(false);
1227            }
1228        }
1229    } // checkNormalizationAfterRemove(ChildNode)
1230

1231    //
1232
// Serialization methods
1233
//
1234

1235    /** Serialize object. */
1236    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1237
1238        // synchronize chilren
1239
if (needsSyncChildren()) {
1240            synchronizeChildren();
1241        }
1242        // write object
1243
out.defaultWriteObject();
1244
1245    } // writeObject(ObjectOutputStream)
1246

1247    /** Deserialize object. */
1248    private void readObject(ObjectInputStream JavaDoc ois)
1249        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1250
1251        // perform default deseralization
1252
ois.defaultReadObject();
1253
1254        // hardset synchildren - so we don't try to sync -
1255
// it does not make any sense to try to synchildren when we just
1256
// deserialize object.
1257
needsSyncChildren(false);
1258
1259    } // readObject(ObjectInputStream)
1260

1261
1262} // class AttrImpl
1263
Popular Tags