KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2001 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 org.enhydra.apache.xerces.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
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     //
163
// Data
164
//
165

166     /** This can either be a String or the first child node. */
167     protected Object JavaDoc value = null;
168
169     /** Attribute name. */
170     protected String JavaDoc name;
171
172     protected static TextImpl textNode = null;
173
174     //
175
// Constructors
176
//
177

178     /**
179      * Attribute has no public constructor. Please use the factory
180      * method in the Document class.
181      */

182     protected AttrImpl(CoreDocumentImpl ownerDocument, String JavaDoc name) {
183         super(ownerDocument);
184         this.name = name;
185         /** False for default attributes. */
186         isSpecified(true);
187         hasStringValue(true);
188     }
189
190     // for AttrNSImpl
191
protected AttrImpl() {}
192
193     // create a real text node as child if we don't have one yet
194
protected void makeChildNode() {
195         if (hasStringValue()) {
196             if (value != null) {
197                 TextImpl text =
198                     (TextImpl) ownerDocument().createTextNode((String JavaDoc) value);
199                 value = text;
200                 text.isFirstChild(true);
201                 text.previousSibling = text;
202                 text.ownerNode = this;
203                 text.isOwned(true);
204             }
205             hasStringValue(false);
206         }
207     }
208
209     /**
210      * NON-DOM
211      * set the ownerDocument of this node and its children
212      */

213     void setOwnerDocument(CoreDocumentImpl doc) {
214         if (needsSyncChildren()) {
215             synchronizeChildren();
216         }
217         super.setOwnerDocument(doc);
218         if (!hasStringValue()) {
219             for (ChildNode child = (ChildNode) value;
220                  child != null; child = child.nextSibling) {
221                 child.setOwnerDocument(doc);
222             }
223         }
224     }
225
226     //
227
// Node methods
228
//
229

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

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

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

279     public void setNodeValue(String JavaDoc value) throws DOMException JavaDoc {
280         setValue(value);
281     }
282
283     /**
284      * In Attribute objects, NodeValue is considered a synonym for
285      * Value.
286      *
287      * @see #getValue()
288      */

289     public String JavaDoc getNodeValue() {
290         return getValue();
291     }
292
293     //
294
// Attr methods
295
//
296

297     /**
298      * In Attributes, NodeName is considered a synonym for the
299      * attribute's Name
300      */

301     public String JavaDoc getName() {
302
303         if (needsSyncData()) {
304             synchronizeData();
305         }
306         return name;
307
308     } // getName():String
309

310     /**
311      * The DOM doesn't clearly define what setValue(null) means. I've taken it
312      * as "remove all children", which from outside should appear
313      * similar to setting it to the empty string.
314      */

315     public void setValue(String JavaDoc newvalue) {
316
317         if (isReadOnly()) {
318             throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
319                                    "DOM001 Modification not allowed");
320         }
321         CoreDocumentImpl ownerDocument = ownerDocument();
322         String JavaDoc oldvalue = "";
323         if (ownerDocument.getMutationEvents()) {
324             // Can no longer just discard the kids; they may have
325
// event listeners waiting for them to disconnect.
326
if (needsSyncChildren()) {
327                 synchronizeChildren();
328             }
329             if (value != null) {
330                 if (hasStringValue()) {
331                     oldvalue = (String JavaDoc) value;
332                     // create an actual text node as our child so
333
// that we can use it in the event
334
if (textNode == null) {
335                         textNode = (TextImpl)
336                             ownerDocument.createTextNode((String JavaDoc) value);
337                     }
338                     else {
339                         textNode.data = (String JavaDoc) value;
340                     }
341                     value = textNode;
342                     textNode.isFirstChild(true);
343                     textNode.previousSibling = textNode;
344                     textNode.ownerNode = this;
345                     textNode.isOwned(true);
346                     hasStringValue(false);
347                     internalRemoveChild(textNode, true);
348                 }
349                 else {
350                     oldvalue = getValue();
351                     while (value != null) {
352                         internalRemoveChild((Node JavaDoc) value, true);
353                     }
354                 }
355             }
356         }
357         else
358         {
359             // simply discard children if any
360
if (!hasStringValue() && value != null) {
361                 // remove ref from first child to last child
362
ChildNode firstChild = (ChildNode) value;
363                 firstChild.previousSibling = null;
364                 firstChild.isFirstChild(false);
365             }
366             // then remove ref to current value
367
value = null;
368             needsSyncChildren(false);
369         }
370
371         // Create and add the new one, generating only non-aggregate events
372
// (There are no listeners on the new Text, but there may be
373
// capture/bubble listeners on the Attr.
374
// Note that aggregate events are NOT dispatched here,
375
// since we need to combine the remove and insert.
376
isSpecified(true);
377         if (ownerDocument.getMutationEvents()) {
378             // if there are any event handlers create a real node
379
internalInsertBefore(ownerDocument.createTextNode(newvalue),
380                                  null, true);
381             hasStringValue(false);
382             // notify document
383
ownerDocument.modifiedAttrValue(this, oldvalue);
384         } else {
385             // directly store the string
386
value = newvalue;
387             hasStringValue(true);
388             changed();
389         }
390
391     } // setValue(String)
392

393     /**
394      * The "string value" of an Attribute is its text representation,
395      * which in turn is a concatenation of the string values of its children.
396      */

397     public String JavaDoc getValue() {
398
399         if (needsSyncChildren()) {
400             synchronizeChildren();
401         }
402         if (value == null) {
403             return "";
404         }
405         if (hasStringValue()) {
406             return (String JavaDoc) value;
407         }
408         ChildNode firstChild = ((ChildNode) value);
409         ChildNode node = firstChild.nextSibling;
410         if (node == null) {
411             return firstChild.getNodeValue();
412         }
413         StringBuffer JavaDoc value = new StringBuffer JavaDoc(firstChild.getNodeValue());
414         while (node != null) {
415             value.append(node.getNodeValue());
416             node = node.nextSibling;
417         }
418         return value.toString();
419
420     } // getValue():String
421

422     /**
423      * The "specified" flag is true if and only if this attribute's
424      * value was explicitly specified in the original document. Note that
425      * the implementation, not the user, is in charge of this
426      * property. If the user asserts an Attribute value (even if it ends
427      * up having the same value as the default), it is considered a
428      * specified attribute. If you really want to revert to the default,
429      * delete the attribute from the Element, and the Implementation will
430      * re-assert the default (if any) in its place, with the appropriate
431      * specified=false setting.
432      */

433     public boolean getSpecified() {
434
435         if (needsSyncData()) {
436             synchronizeData();
437         }
438         return isSpecified();
439
440     } // getSpecified():boolean
441

442     //
443
// Attr2 methods
444
//
445

446     /**
447      * Returns the element node that this attribute is associated with,
448      * or null if the attribute has not been added to an element.
449      *
450      * @see #getOwnerElement
451      *
452      * @deprecated Previous working draft of DOM Level 2. New method
453      * is <tt>getOwnerElement()</tt>.
454      */

455     public Element JavaDoc getElement() {
456         // if we have an owner, ownerNode is our ownerElement, otherwise it's
457
// our ownerDocument and we don't have an ownerElement
458
return (Element JavaDoc) (isOwned() ? ownerNode : null);
459     }
460
461     /**
462      * Returns the element node that this attribute is associated with,
463      * or null if the attribute has not been added to an element.
464      *
465      * @since WD-DOM-Level-2-19990719
466      */

467     public Element JavaDoc getOwnerElement() {
468         // if we have an owner, ownerNode is our ownerElement, otherwise it's
469
// our ownerDocument and we don't have an ownerElement
470
return (Element JavaDoc) (isOwned() ? ownerNode : null);
471     }
472     
473     public void normalize() {
474
475         // No need to normalize if already normalized or
476
// if value is kept as a String.
477
if (isNormalized() || hasStringValue())
478             return;
479
480         Node JavaDoc kid, next;
481         ChildNode firstChild = (ChildNode)value;
482         for (kid = firstChild; kid != null; kid = next) {
483             next = kid.getNextSibling();
484
485             // If kid is a text node, we need to check for one of two
486
// conditions:
487
// 1) There is an adjacent text node
488
// 2) There is no adjacent text node, but kid is
489
// an empty text node.
490
if ( kid.getNodeType() == Node.TEXT_NODE )
491             {
492                 // If an adjacent text node, merge it with kid
493
if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
494                 {
495                     ((Text JavaDoc)kid).appendData(next.getNodeValue());
496                     removeChild( next );
497                     next = kid; // Don't advance; there might be another.
498
}
499                 else
500                 {
501                     // If kid is empty, remove it
502
if ( kid.getNodeValue().length()==0 )
503                         removeChild( kid );
504                 }
505             }
506         }
507
508         isNormalized(true);
509     } // normalize()
510

511     //
512
// Public methods
513
//
514

515     /** NON-DOM, for use by parser */
516     public void setSpecified(boolean arg) {
517
518         if (needsSyncData()) {
519             synchronizeData();
520         }
521         isSpecified(arg);
522
523     } // setSpecified(boolean)
524

525     //
526
// Object methods
527
//
528

529     /** NON-DOM method for debugging convenience */
530     public String JavaDoc toString() {
531         return getName() + "=" + "\"" + getValue() + "\"";
532     }
533
534     /**
535      * Test whether this node has any children. Convenience shorthand
536      * for (Node.getFirstChild()!=null)
537      */

538     public boolean hasChildNodes() {
539         if (needsSyncChildren()) {
540             synchronizeChildren();
541         }
542         return value != null;
543     }
544
545     /**
546      * Obtain a NodeList enumerating all children of this node. If there
547      * are none, an (initially) empty NodeList is returned.
548      * <p>
549      * NodeLists are "live"; as children are added/removed the NodeList
550      * will immediately reflect those changes. Also, the NodeList refers
551      * to the actual nodes, so changes to those nodes made via the DOM tree
552      * will be reflected in the NodeList and vice versa.
553      * <p>
554      * In this implementation, Nodes implement the NodeList interface and
555      * provide their own getChildNodes() support. Other DOMs may solve this
556      * differently.
557      */

558     public NodeList JavaDoc getChildNodes() {
559         // JKESS: KNOWN ISSUE HERE
560

561         if (needsSyncChildren()) {
562             synchronizeChildren();
563         }
564         return this;
565
566     } // getChildNodes():NodeList
567

568     /** The first child of this Node, or null if none. */
569     public Node JavaDoc getFirstChild() {
570
571         if (needsSyncChildren()) {
572             synchronizeChildren();
573         }
574         makeChildNode();
575         return (Node JavaDoc) value;
576
577     } // getFirstChild():Node
578

579     /** The last child of this Node, or null if none. */
580     public Node JavaDoc getLastChild() {
581
582         if (needsSyncChildren()) {
583             synchronizeChildren();
584         }
585         return lastChild();
586
587     } // getLastChild():Node
588

589     final ChildNode lastChild() {
590         // last child is stored as the previous sibling of first child
591
makeChildNode();
592         return value != null ? ((ChildNode) value).previousSibling : null;
593     }
594
595     final void lastChild(ChildNode node) {
596         // store lastChild as previous sibling of first child
597
if (value != null) {
598             ((ChildNode) value).previousSibling = node;
599         }
600     }
601
602     /**
603      * Move one or more node(s) to our list of children. Note that this
604      * implicitly removes them from their previous parent.
605      *
606      * @param newChild The Node to be moved to our subtree. As a
607      * convenience feature, inserting a DocumentNode will instead insert
608      * all its children.
609      *
610      * @param refChild Current child which newChild should be placed
611      * immediately before. If refChild is null, the insertion occurs
612      * after all existing Nodes, like appendChild().
613      *
614      * @returns newChild, in its new state (relocated, or emptied in the
615      * case of DocumentNode.)
616      *
617      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
618      * type that shouldn't be a child of this node, or if newChild is an
619      * ancestor of this node.
620      *
621      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
622      * different owner document than we do.
623      *
624      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
625      * this node.
626      *
627      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
628      * read-only.
629      */

630     public Node JavaDoc insertBefore(Node JavaDoc newChild, Node JavaDoc refChild)
631         throws DOMException JavaDoc {
632         // Tail-call; optimizer should be able to do good things with.
633
return internalInsertBefore(newChild, refChild, false);
634     } // insertBefore(Node,Node):Node
635

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

641     Node JavaDoc internalInsertBefore(Node JavaDoc newChild, Node JavaDoc refChild, boolean replace)
642         throws DOMException JavaDoc {
643
644         CoreDocumentImpl ownerDocument = ownerDocument();
645         boolean errorChecking = ownerDocument.errorChecking;
646
647         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
648             // SLOW BUT SAFE: We could insert the whole subtree without
649
// juggling so many next/previous pointers. (Wipe out the
650
// parent's child-list, patch the parent pointers, set the
651
// ends of the list.) But we know some subclasses have special-
652
// case behavior they add to insertBefore(), so we don't risk it.
653
// This approch also takes fewer bytecodes.
654

655             // NOTE: If one of the children is not a legal child of this
656
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
657
// have been transferred. (Alternative behaviors would be to
658
// reparent up to the first failure point or reparent all those
659
// which are acceptable to the target node, neither of which is
660
// as robust. PR-DOM-0818 isn't entirely clear on which it
661
// recommends?????
662

663             // No need to check kids for right-document; if they weren't,
664
// they wouldn't be kids of that DocFrag.
665
if (errorChecking) {
666                 for (Node JavaDoc kid = newChild.getFirstChild(); // Prescan
667
kid != null; kid = kid.getNextSibling()) {
668
669                     if (!ownerDocument.isKidOK(this, kid)) {
670                         throw new DOMException JavaDoc(
671                                            DOMException.HIERARCHY_REQUEST_ERR,
672                                            "DOM006 Hierarchy request error");
673                     }
674                 }
675             }
676
677             while (newChild.hasChildNodes()) {
678                 insertBefore(newChild.getFirstChild(), refChild);
679             }
680             return newChild;
681         }
682
683         if (newChild == refChild) {
684             // stupid case that must be handled as a no-op triggering events...
685
refChild = refChild.getNextSibling();
686             removeChild(newChild);
687             insertBefore(newChild, refChild);
688             return newChild;
689         }
690
691         if (needsSyncChildren()) {
692             synchronizeChildren();
693         }
694
695         if (errorChecking) {
696             if (isReadOnly()) {
697                 throw new DOMException JavaDoc(
698                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
699                                        "DOM001 Modification not allowed");
700             }
701             if (newChild.getOwnerDocument() != ownerDocument) {
702                 throw new DOMException JavaDoc(DOMException.WRONG_DOCUMENT_ERR,
703                                        "DOM005 Wrong document");
704             }
705             if (!ownerDocument.isKidOK(this, newChild)) {
706                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
707                                        "DOM006 Hierarchy request error");
708             }
709             // refChild must be a child of this node (or null)
710
if (refChild != null && refChild.getParentNode() != this) {
711                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
712                                        "DOM008 Not found");
713             }
714
715             // Prevent cycles in the tree
716
// newChild cannot be ancestor of this Node,
717
// and actually cannot be this
718
boolean treeSafe = true;
719             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
720             {
721                 treeSafe = newChild != a;
722             }
723             if (!treeSafe) {
724                 throw new DOMException JavaDoc(DOMException.HIERARCHY_REQUEST_ERR,
725                                        "DOM006 Hierarchy request error");
726             }
727         }
728
729         makeChildNode(); // make sure we have a node and not a string
730

731         // notify document
732
ownerDocument.insertingNode(this, replace);
733
734         // Convert to internal type, to avoid repeated casting
735
ChildNode newInternal = (ChildNode)newChild;
736
737         Node JavaDoc oldparent = newInternal.parentNode();
738         if (oldparent != null) {
739             oldparent.removeChild(newInternal);
740         }
741
742         // Convert to internal type, to avoid repeated casting
743
ChildNode refInternal = (ChildNode) refChild;
744
745         // Attach up
746
newInternal.ownerNode = this;
747         newInternal.isOwned(true);
748
749         // Attach before and after
750
// Note: firstChild.previousSibling == lastChild!!
751
ChildNode firstChild = (ChildNode) value;
752         if (firstChild == null) {
753             // this our first and only child
754
value = newInternal; // firstchild = newInternal;
755
newInternal.isFirstChild(true);
756             newInternal.previousSibling = newInternal;
757         }
758         else {
759             if (refInternal == null) {
760                 // this is an append
761
ChildNode lastChild = firstChild.previousSibling;
762                 lastChild.nextSibling = newInternal;
763                 newInternal.previousSibling = lastChild;
764                 firstChild.previousSibling = newInternal;
765             }
766             else {
767                 // this is an insert
768
if (refChild == firstChild) {
769                     // at the head of the list
770
firstChild.isFirstChild(false);
771                     newInternal.nextSibling = firstChild;
772                     newInternal.previousSibling = firstChild.previousSibling;
773                     firstChild.previousSibling = newInternal;
774                     value = newInternal; // firstChild = newInternal;
775
newInternal.isFirstChild(true);
776                 }
777                 else {
778                     // somewhere in the middle
779
ChildNode prev = refInternal.previousSibling;
780                     newInternal.nextSibling = refInternal;
781                     prev.nextSibling = newInternal;
782                     refInternal.previousSibling = newInternal;
783                     newInternal.previousSibling = prev;
784                 }
785             }
786         }
787
788         changed();
789
790         // notify document
791
ownerDocument.insertedNode(this, newInternal, replace);
792
793         checkNormalizationAfterInsert(newInternal);
794
795         return newChild;
796
797     } // internalInsertBefore(Node,Node,int):Node
798

799     /**
800      * Remove a child from this Node. The removed child's subtree
801      * remains intact so it may be re-inserted elsewhere.
802      *
803      * @return oldChild, in its new state (removed).
804      *
805      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
806      * this node.
807      *
808      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
809      * read-only.
810      */

811     public Node JavaDoc removeChild(Node JavaDoc oldChild)
812         throws DOMException JavaDoc {
813         // Tail-call, should be optimizable
814
if (hasStringValue()) {
815             // we don't have any child per say so it can't be one of them!
816
throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
817                                    "DOM008 Not found");
818         }
819         return internalRemoveChild(oldChild, false);
820     } // removeChild(Node) :Node
821

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

827     Node JavaDoc internalRemoveChild(Node JavaDoc oldChild, boolean replace)
828         throws DOMException JavaDoc {
829
830         CoreDocumentImpl ownerDocument = ownerDocument();
831         if (ownerDocument.errorChecking) {
832             if (isReadOnly()) {
833                 throw new DOMException JavaDoc(
834                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
835                                      "DOM001 Modification not allowed");
836             }
837             if (oldChild != null && oldChild.getParentNode() != this) {
838                 throw new DOMException JavaDoc(DOMException.NOT_FOUND_ERR,
839                                        "DOM008 Not found");
840             }
841         }
842
843         ChildNode oldInternal = (ChildNode) oldChild;
844
845         // notify document
846
ownerDocument.removingNode(this, oldInternal, replace);
847
848         // Patch linked list around oldChild
849
// Note: lastChild == firstChild.previousSibling
850
if (oldInternal == value) { // oldInternal == firstChild
851
// removing first child
852
oldInternal.isFirstChild(false);
853             // next line is: firstChild = oldInternal.nextSibling
854
value = oldInternal.nextSibling;
855             ChildNode firstChild = (ChildNode) value;
856             if (firstChild != null) {
857                 firstChild.isFirstChild(true);
858                 firstChild.previousSibling = oldInternal.previousSibling;
859             }
860         } else {
861             ChildNode prev = oldInternal.previousSibling;
862             ChildNode next = oldInternal.nextSibling;
863             prev.nextSibling = next;
864             if (next == null) {
865                 // removing last child
866
ChildNode firstChild = (ChildNode) value;
867                 firstChild.previousSibling = prev;
868             } else {
869                 // removing some other child in the middle
870
next.previousSibling = prev;
871             }
872         }
873
874         // Save previous sibling for normalization checking.
875
ChildNode oldPreviousSibling = oldInternal.previousSibling();
876
877         // Remove oldInternal's references to tree
878
oldInternal.ownerNode = ownerDocument;
879         oldInternal.isOwned(false);
880         oldInternal.nextSibling = null;
881         oldInternal.previousSibling = null;
882
883         changed();
884
885         // notify document
886
ownerDocument.removedNode(this, replace);
887
888         checkNormalizationAfterRemove(oldPreviousSibling);
889
890         return oldInternal;
891
892     } // internalRemoveChild(Node,int):Node
893

894     /**
895      * Make newChild occupy the location that oldChild used to
896      * have. Note that newChild will first be removed from its previous
897      * parent, if any. Equivalent to inserting newChild before oldChild,
898      * then removing oldChild.
899      *
900      * @returns oldChild, in its new state (removed).
901      *
902      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
903      * type that shouldn't be a child of this node, or if newChild is
904      * one of our ancestors.
905      *
906      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
907      * different owner document than we do.
908      *
909      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
910      * this node.
911      *
912      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
913      * read-only.
914      */

915     public Node JavaDoc replaceChild(Node JavaDoc newChild, Node JavaDoc oldChild)
916         throws DOMException JavaDoc {
917
918         makeChildNode();
919
920         // If Mutation Events are being generated, this operation might
921
// throw aggregate events twice when modifying an Attr -- once
922
// on insertion and once on removal. DOM Level 2 does not specify
923
// this as either desirable or undesirable, but hints that
924
// aggregations should be issued only once per user request.
925

926         // notify document
927
CoreDocumentImpl ownerDocument = ownerDocument();
928         ownerDocument.replacingNode(this);
929
930         internalInsertBefore(newChild, oldChild, true);
931         if (newChild != oldChild) {
932             internalRemoveChild(oldChild, true);
933         }
934
935         // notify document
936
ownerDocument.replacedNode(this);
937
938         return oldChild;
939     }
940
941     //
942
// NodeList methods
943
//
944

945     /**
946      * NodeList method: Count the immediate children of this node
947      * @return int
948      */

949     public int getLength() {
950
951         if (hasStringValue()) {
952             return 1;
953         }
954         ChildNode node = (ChildNode) value;
955         int length = 0;
956         for (; node != null; node = node.nextSibling) {
957             length++;
958         }
959         return length;
960
961     } // getLength():int
962

963     /**
964      * NodeList method: Return the Nth immediate child of this node, or
965      * null if the index is out of bounds.
966      * @return org.w3c.dom.Node
967      * @param Index int
968      */

969     public Node JavaDoc item(int index) {
970
971         if (hasStringValue()) {
972             if (index != 0 || value == null) {
973                 return null;
974             }
975             else {
976                 makeChildNode();
977                 return (Node JavaDoc) value;
978             }
979         }
980         ChildNode node = (ChildNode) value;
981         for (int i = 0; i < index && node != null; i++) {
982             node = node.nextSibling;
983         }
984         return node;
985
986     } // item(int):Node
987

988     //
989
// DOM2: methods, getters, setters
990
//
991

992     //
993
// Public methods
994
//
995

996     /**
997      * Override default behavior so that if deep is true, children are also
998      * toggled.
999      * @see Node
1000     * <P>
1001     * Note: this will not change the state of an EntityReference or its
1002     * children, which are always read-only.
1003     */

1004    public void setReadOnly(boolean readOnly, boolean deep) {
1005
1006        super.setReadOnly(readOnly, deep);
1007
1008        if (deep) {
1009
1010            if (needsSyncChildren()) {
1011                synchronizeChildren();
1012            }
1013
1014            if (hasStringValue()) {
1015                return;
1016            }
1017            // Recursively set kids
1018
for (ChildNode mykid = (ChildNode) value;
1019                 mykid != null;
1020                 mykid = mykid.nextSibling) {
1021                if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1022                    mykid.setReadOnly(readOnly,true);
1023                }
1024            }
1025        }
1026    } // setReadOnly(boolean,boolean)
1027

1028    //
1029
// Protected methods
1030
//
1031

1032    /**
1033     * Override this method in subclass to hook in efficient
1034     * internal data structure.
1035     */

1036    protected void synchronizeChildren() {
1037        // By default just change the flag to avoid calling this method again
1038
needsSyncChildren(false);
1039    }
1040
1041    /**
1042     * Checks the normalized state of this node after inserting a child.
1043     * If the inserted child causes this node to be unnormalized, then this
1044     * node is flagged accordingly.
1045     * The conditions for changing the normalized state are:
1046     * <ul>
1047     * <li>The inserted child is a text node and one of its adjacent siblings
1048     * is also a text node.
1049     * <li>The inserted child is is itself unnormalized.
1050     * </ul>
1051     *
1052     * @param insertedChild the child node that was inserted into this node
1053     *
1054     * @throws NullPointerException if the inserted child is <code>null</code>
1055     */

1056    void checkNormalizationAfterInsert(ChildNode insertedChild) {
1057        // See if insertion caused this node to be unnormalized.
1058
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1059            ChildNode prev = insertedChild.previousSibling();
1060            ChildNode next = insertedChild.nextSibling;
1061            // If an adjacent sibling of the new child is a text node,
1062
// flag this node as unnormalized.
1063
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
1064                (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1065                isNormalized(false);
1066            }
1067        }
1068        else {
1069            // If the new child is not normalized,
1070
// then this node is inherently not normalized.
1071
if (!insertedChild.isNormalized()) {
1072                isNormalized(false);
1073            }
1074        }
1075    } // checkNormalizationAfterInsert(ChildNode)
1076

1077    /**
1078     * Checks the normalized of this node after removing a child.
1079     * If the removed child causes this node to be unnormalized, then this
1080     * node is flagged accordingly.
1081     * The conditions for changing the normalized state are:
1082     * <ul>
1083     * <li>The removed child had two adjacent siblings that were text nodes.
1084     * </ul>
1085     *
1086     * @param previousSibling the previous sibling of the removed child, or
1087     * <code>null</code>
1088     */

1089    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1090        // See if removal caused this node to be unnormalized.
1091
// If the adjacent siblings of the removed child were both text nodes,
1092
// flag this node as unnormalized.
1093
if (previousSibling != null &&
1094            previousSibling.getNodeType() == Node.TEXT_NODE) {
1095
1096            ChildNode next = previousSibling.nextSibling;
1097            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1098                isNormalized(false);
1099            }
1100        }
1101    } // checkNormalizationAfterRemove(ChildNode)
1102

1103    //
1104
// Serialization methods
1105
//
1106

1107    /** Serialize object. */
1108    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1109
1110        // synchronize chilren
1111
if (needsSyncChildren()) {
1112            synchronizeChildren();
1113        }
1114        // write object
1115
out.defaultWriteObject();
1116
1117    } // writeObject(ObjectOutputStream)
1118

1119    /** Deserialize object. */
1120    private void readObject(ObjectInputStream JavaDoc ois)
1121        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1122
1123        // perform default deseralization
1124
ois.defaultReadObject();
1125
1126        // hardset synchildren - so we don't try to sync -
1127
// it does not make any sense to try to synchildren when we just
1128
// deserialize object.
1129
needsSyncChildren(false);
1130
1131    } // readObject(ObjectInputStream)
1132

1133
1134    /* (non-Javadoc)
1135     * @see org.w3c.dom.Attr#getSchemaTypeInfo()
1136     */

1137    public TypeInfo JavaDoc getSchemaTypeInfo() {
1138        // TODO Auto-generated method stub
1139
return null;
1140    }
1141    /* (non-Javadoc)
1142     * @see org.w3c.dom.Attr#isId()
1143     */

1144    public boolean isId() {
1145        // TODO Auto-generated method stub
1146
return false;
1147    }
1148
1149} // class AttrImpl
1150
Popular Tags