KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xquark > xpath > datamodel > xerces > dom > AttrImpl


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

144 public class AttrImpl
145     extends NodeImpl
146     implements Attr {
147
148     //
149
// Constants
150
//
151

152     /** Serialization version. */
153     static final long serialVersionUID = 7277707688218972102L;
154
155     //
156
// Data
157
//
158

159     /** This can either be a String or the first child node. */
160     protected Object JavaDoc value = null;
161
162     /** Attribute name. */
163     protected String JavaDoc name;
164
165     protected static TextImpl textNode = null;
166
167     //
168
// Constructors
169
//
170

171     /**
172      * Attribute has no public constructor. Please use the factory
173      * method in the Document class.
174      */

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

206     void setOwnerDocument(DocumentImpl doc) {
207         if (needsSyncChildren()) {
208             synchronizeChildren();
209         }
210         super.setOwnerDocument(doc);
211         if (!hasStringValue()) {
212             for (ChildNode child = (ChildNode) value;
213                  child != null; child = child.nextSibling) {
214                 child.setOwnerDocument(doc);
215             }
216         }
217     }
218
219     //
220
// Node methods
221
//
222

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

252     public short getNodeType() {
253         return Node.ATTRIBUTE_NODE;
254     }
255
256     /**
257      * Returns the attribute name
258      */

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

272     public void setNodeValue(String JavaDoc value) throws DOMException {
273         setValue(value);
274     }
275
276     /**
277      * In Attribute objects, NodeValue is considered a synonym for
278      * Value.
279      *
280      * @see #getValue()
281      */

282     public String JavaDoc getNodeValue() {
283         return getValue();
284     }
285
286     //
287
// Attr methods
288
//
289

290     /**
291      * In Attributes, NodeName is considered a synonym for the
292      * attribute's Name
293      */

294     public String JavaDoc getName() {
295
296         if (needsSyncData()) {
297             synchronizeData();
298         }
299         return name;
300
301     } // getName():String
302

303     /**
304      * The DOM doesn't clearly define what setValue(null) means. I've taken it
305      * as "remove all children", which from outside should appear
306      * similar to setting it to the empty string.
307      */

308     public void setValue(String JavaDoc newvalue) {
309
310         if (isReadOnly()) {
311             throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
312                                    "DOM001 Modification not allowed");
313         }
314             
315         LCount lc=null;
316         String JavaDoc oldvalue="";
317         DocumentImpl ownerDocument = ownerDocument();
318         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
319         {
320             // MUTATION PREPROCESSING AND PRE-EVENTS:
321
// Only DOMAttrModified need be produced directly.
322
// It needs the previous value. Note that this may be
323
// a treewalk, so I've put it under the conditional.
324
lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
325             if(lc.captures+lc.bubbles+lc.defaults>0 && ownerNode!=null)
326             {
327                oldvalue=getValue();
328             }
329             
330         } // End mutation preprocessing
331

332         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
333         {
334             // Can no longer just discard the kids; they may have
335
// event listeners waiting for them to disconnect.
336
if (needsSyncChildren()) {
337                 synchronizeChildren();
338             }
339             if (value != null) {
340                 if (hasStringValue()) {
341                     // temporarily sets an actual text node as our child so
342
// that we can use it in the event
343
if (textNode == null) {
344                         textNode = (TextImpl)
345                             ownerDocument.createTextNode((String JavaDoc) value);
346                     }
347                     else {
348                         textNode.data = (String JavaDoc) value;
349                     }
350                     value = textNode;
351                     textNode.isFirstChild(true);
352                     textNode.previousSibling = textNode;
353                     textNode.ownerNode = this;
354                     textNode.isOwned(true);
355                     hasStringValue(false);
356                     internalRemoveChild(textNode, MUTATION_LOCAL);
357                 }
358                 else {
359                     while (value != null) {
360                         internalRemoveChild((Node) value, MUTATION_LOCAL);
361                     }
362                 }
363             }
364         }
365         else
366         {
367             // simply discard children if any
368
if (!hasStringValue() && value != null) {
369                 // remove ref from first child to last child
370
ChildNode firstChild = (ChildNode) value;
371                 firstChild.previousSibling = null;
372                 firstChild.isFirstChild(false);
373             }
374             // then remove ref to current value
375
value = null;
376             needsSyncChildren(false);
377         }
378
379         // Create and add the new one, generating only non-aggregate events
380
// (There are no listeners on the new Text, but there may be
381
// capture/bubble listeners on the Attr.
382
// Note that aggregate events are NOT dispatched here,
383
// since we need to combine the remove and insert.
384
isSpecified(true);
385         if(MUTATIONEVENTS && ownerDocument.mutationEvents) {
386             // if there are any event handlers create a real node
387
internalInsertBefore(ownerDocument.createTextNode(newvalue),
388                                  null, MUTATION_LOCAL);
389             hasStringValue(false);
390         } else {
391             // directly store the string
392
value = newvalue;
393             hasStringValue(true);
394         }
395         
396         changed(); // ***** Is this redundant?
397

398         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
399         {
400             // MUTATION POST-EVENTS:
401
dispatchAggregateEvents(this,oldvalue,MutationEvent.MODIFICATION);
402         }
403         
404     } // setValue(String)
405

406     /**
407      * The "string value" of an Attribute is its text representation,
408      * which in turn is a concatenation of the string values of its children.
409      */

410     public String JavaDoc getValue() {
411
412         if (needsSyncChildren()) {
413             synchronizeChildren();
414         }
415         if (value == null) {
416             return "";
417         }
418         if (hasStringValue()) {
419             return (String JavaDoc) value;
420         }
421         ChildNode firstChild = ((ChildNode) value);
422         ChildNode node = firstChild.nextSibling;
423         if (node == null) {
424             return firstChild.getNodeValue();
425         }
426         StringBuffer JavaDoc value = new StringBuffer JavaDoc(firstChild.getNodeValue());
427         while (node != null) {
428             value.append(node.getNodeValue());
429             node = node.nextSibling;
430         }
431         return value.toString();
432
433     } // getValue():String
434

435     /**
436      * The "specified" flag is true if and only if this attribute's
437      * value was explicitly specified in the original document. Note that
438      * the implementation, not the user, is in charge of this
439      * property. If the user asserts an Attribute value (even if it ends
440      * up having the same value as the default), it is considered a
441      * specified attribute. If you really want to revert to the default,
442      * delete the attribute from the Element, and the Implementation will
443      * re-assert the default (if any) in its place, with the appropriate
444      * specified=false setting.
445      */

446     public boolean getSpecified() {
447
448         if (needsSyncData()) {
449             synchronizeData();
450         }
451         return isSpecified();
452
453     } // getSpecified():boolean
454

455     //
456
// Attr2 methods
457
//
458

459     /**
460      * Returns the element node that this attribute is associated with,
461      * or null if the attribute has not been added to an element.
462      *
463      * @see #getOwnerElement
464      *
465      * @deprecated Previous working draft of DOM Level 2. New method
466      * is <tt>getOwnerElement()</tt>.
467      */

468     public Element getElement() {
469         // if we have an owner, ownerNode is our ownerElement, otherwise it's
470
// our ownerDocument and we don't have an ownerElement
471
return (Element) (isOwned() ? ownerNode : null);
472     }
473
474     /**
475      * Returns the element node that this attribute is associated with,
476      * or null if the attribute has not been added to an element.
477      *
478      * @since WD-DOM-Level-2-19990719
479      */

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

524     //
525
// Public methods
526
//
527

528     /** NON-DOM, for use by parser */
529     public void setSpecified(boolean arg) {
530
531         if (needsSyncData()) {
532             synchronizeData();
533         }
534         isSpecified(arg);
535
536     } // setSpecified(boolean)
537

538     //
539
// Object methods
540
//
541

542     /** NON-DOM method for debugging convenience */
543     public String JavaDoc toString() {
544         return getName() + "=" + "\"" + getValue() + "\"";
545     }
546
547     /**
548      * Test whether this node has any children. Convenience shorthand
549      * for (Node.getFirstChild()!=null)
550      */

551     public boolean hasChildNodes() {
552         if (needsSyncChildren()) {
553             synchronizeChildren();
554         }
555         return value != null;
556     }
557
558     /**
559      * Obtain a NodeList enumerating all children of this node. If there
560      * are none, an (initially) empty NodeList is returned.
561      * <p>
562      * NodeLists are "live"; as children are added/removed the NodeList
563      * will immediately reflect those changes. Also, the NodeList refers
564      * to the actual nodes, so changes to those nodes made via the DOM tree
565      * will be reflected in the NodeList and vice versa.
566      * <p>
567      * In this implementation, Nodes implement the NodeList interface and
568      * provide their own getChildNodes() support. Other DOMs may solve this
569      * differently.
570      */

571     public NodeList getChildNodes() {
572         // JKESS: KNOWN ISSUE HERE
573

574         if (needsSyncChildren()) {
575             synchronizeChildren();
576         }
577         return this;
578
579     } // getChildNodes():NodeList
580

581     /** The first child of this Node, or null if none. */
582     public Node getFirstChild() {
583
584         if (needsSyncChildren()) {
585             synchronizeChildren();
586         }
587         makeChildNode();
588         return (Node) value;
589
590     } // getFirstChild():Node
591

592     /** The last child of this Node, or null if none. */
593     public Node getLastChild() {
594
595         if (needsSyncChildren()) {
596             synchronizeChildren();
597         }
598         return lastChild();
599
600     } // getLastChild():Node
601

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

643     public Node insertBefore(Node newChild, Node refChild)
644         throws DOMException {
645         // Tail-call; optimizer should be able to do good things with.
646
return internalInsertBefore(newChild,refChild,MUTATION_ALL);
647     } // insertBefore(Node,Node):Node
648

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

654     Node internalInsertBefore(Node newChild, Node refChild,int mutationMask)
655         throws DOMException {
656
657         DocumentImpl ownerDocument = ownerDocument();
658         boolean errorChecking = ownerDocument.errorChecking;
659
660         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
661             // SLOW BUT SAFE: We could insert the whole subtree without
662
// juggling so many next/previous pointers. (Wipe out the
663
// parent's child-list, patch the parent pointers, set the
664
// ends of the list.) But we know some subclasses have special-
665
// case behavior they add to insertBefore(), so we don't risk it.
666
// This approch also takes fewer bytecodes.
667

668             // NOTE: If one of the children is not a legal child of this
669
// node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
670
// have been transferred. (Alternative behaviors would be to
671
// reparent up to the first failure point or reparent all those
672
// which are acceptable to the target node, neither of which is
673
// as robust. PR-DOM-0818 isn't entirely clear on which it
674
// recommends?????
675

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

744         EnclosingAttr enclosingAttr=null;
745         if (MUTATIONEVENTS && ownerDocument.mutationEvents
746             && (mutationMask&MUTATION_AGGREGATE)!=0) {
747             // MUTATION PREPROCESSING
748
// No direct pre-events, but if we're within the scope
749
// of an Attr and DOMAttrModified was requested,
750
// we need to preserve its previous value.
751
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
752             if (lc.captures+lc.bubbles+lc.defaults>0) {
753                 enclosingAttr=getEnclosingAttr();
754             }
755         }
756
757         // Convert to internal type, to avoid repeated casting
758
ChildNode newInternal = (ChildNode)newChild;
759
760         Node oldparent = newInternal.parentNode();
761         if (oldparent != null) {
762             oldparent.removeChild(newInternal);
763         }
764
765         // Convert to internal type, to avoid repeated casting
766
ChildNode refInternal = (ChildNode) refChild;
767
768         // Attach up
769
newInternal.ownerNode = this;
770         newInternal.isOwned(true);
771
772         // Attach before and after
773
// Note: firstChild.previousSibling == lastChild!!
774
ChildNode firstChild = (ChildNode) value;
775         if (firstChild == null) {
776             // this our first and only child
777
value = newInternal; // firstchild = newInternal;
778
newInternal.isFirstChild(true);
779             newInternal.previousSibling = newInternal;
780         }
781         else {
782             if (refInternal == null) {
783                 // this is an append
784
ChildNode lastChild = firstChild.previousSibling;
785                 lastChild.nextSibling = newInternal;
786                 newInternal.previousSibling = lastChild;
787                 firstChild.previousSibling = newInternal;
788             }
789             else {
790                 // this is an insert
791
if (refChild == firstChild) {
792                     // at the head of the list
793
firstChild.isFirstChild(false);
794                     newInternal.nextSibling = firstChild;
795                     newInternal.previousSibling = firstChild.previousSibling;
796                     firstChild.previousSibling = newInternal;
797                     value = newInternal; // firstChild = newInternal;
798
newInternal.isFirstChild(true);
799                 }
800                 else {
801                     // somewhere in the middle
802
ChildNode prev = refInternal.previousSibling;
803                     newInternal.nextSibling = refInternal;
804                     prev.nextSibling = newInternal;
805                     refInternal.previousSibling = newInternal;
806                     newInternal.previousSibling = prev;
807                 }
808             }
809         }
810
811         changed();
812
813         if (MUTATIONEVENTS && ownerDocument.mutationEvents) {
814             // MUTATION POST-EVENTS:
815
// "Local" events (non-aggregated)
816
if ((mutationMask&MUTATION_LOCAL) != 0) {
817                 // New child is told it was inserted, and where
818
LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
819                 if (lc.captures+lc.bubbles+lc.defaults>0) {
820                     MutationEvent JavaDoc me= new MutationEventImpl();
821                     me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED,
822                                          true,false,this,null,
823                                          null,null,(short)0);
824                     newInternal.dispatchEvent(me);
825                 }
826
827                 // If within the Document, tell the subtree it's been added
828
// to the Doc.
829
lc=LCount.lookup(
830                             MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
831                 if (lc.captures+lc.bubbles+lc.defaults>0) {
832                     NodeImpl eventAncestor=this;
833                     if (enclosingAttr!=null)
834                         eventAncestor=
835                             (NodeImpl)(enclosingAttr.node.getOwnerElement());
836                     if (eventAncestor!=null) { // Might have been orphan Attr
837
NodeImpl p=eventAncestor;
838                         while (p!=null) {
839                             eventAncestor=p; // Last non-null ancestor
840
// In this context, ancestry includes
841
// walking back from Attr to Element
842
if (p.getNodeType()==ATTRIBUTE_NODE) {
843                                 p=(ElementImpl)((AttrImpl)p).getOwnerElement();
844                             }
845                             else {
846                                 p=p.parentNode();
847                             }
848                         }
849                         if (eventAncestor.getNodeType()==Node.DOCUMENT_NODE) {
850                             MutationEvent JavaDoc me= new MutationEventImpl();
851                             me.initMutationEvent(MutationEventImpl
852                                               .DOM_NODE_INSERTED_INTO_DOCUMENT,
853                                                  false,false,null,null,
854                                                  null,null,(short)0);
855                             dispatchEventToSubtree(newInternal,me);
856                         }
857                     }
858                 }
859             }
860
861             // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
862
// (Common to most kinds of mutation)
863
if ((mutationMask&MUTATION_AGGREGATE) != 0) {
864                 dispatchAggregateEvents(enclosingAttr);
865             }
866         }
867
868         checkNormalizationAfterInsert(newInternal);
869
870         return newChild;
871
872     } // internalInsertBefore(Node,Node,int):Node
873

874     /**
875      * Remove a child from this Node. The removed child's subtree
876      * remains intact so it may be re-inserted elsewhere.
877      *
878      * @return oldChild, in its new state (removed).
879      *
880      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
881      * this node.
882      *
883      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
884      * read-only.
885      */

886     public Node removeChild(Node oldChild)
887         throws DOMException {
888         // Tail-call, should be optimizable
889
if (hasStringValue()) {
890             // we don't have any child per say so it can't be one of them!
891
throw new DOMException(DOMException.NOT_FOUND_ERR,
892                                    "DOM008 Not found");
893         }
894         return internalRemoveChild(oldChild,MUTATION_ALL);
895     } // removeChild(Node) :Node
896

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

902     Node internalRemoveChild(Node oldChild,int mutationMask)
903         throws DOMException {
904
905         DocumentImpl ownerDocument = ownerDocument();
906         if (ownerDocument.errorChecking) {
907             if (isReadOnly()) {
908                 throw new DOMException(
909                                      DOMException.NO_MODIFICATION_ALLOWED_ERR,
910                                      "DOM001 Modification not allowed");
911             }
912             if (oldChild != null && oldChild.getParentNode() != this) {
913                 throw new DOMException(DOMException.NOT_FOUND_ERR,
914                                        "DOM008 Not found");
915             }
916         }
917
918         // notify document
919
ownerDocument.removedChildNode(oldChild);
920
921         ChildNode oldInternal = (ChildNode) oldChild;
922
923         EnclosingAttr enclosingAttr=null;
924         if(MUTATIONEVENTS && ownerDocument.mutationEvents)
925         {
926             // MUTATION PREPROCESSING AND PRE-EVENTS:
927
// If we're within the scope of an Attr and DOMAttrModified
928
// was requested, we need to preserve its previous value for
929
// that event.
930
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
931             if(lc.captures+lc.bubbles+lc.defaults>0)
932             {
933                 enclosingAttr=getEnclosingAttr();
934             }
935             
936             if( (mutationMask&MUTATION_LOCAL) != 0)
937             {
938                 // Child is told that it is about to be removed
939
lc=LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
940                 if(lc.captures+lc.bubbles+lc.defaults>0)
941                 {
942                     MutationEvent JavaDoc me= new MutationEventImpl();
943                     me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
944                                          true,false,this,null,
945                                          null,null,(short)0);
946                     oldInternal.dispatchEvent(me);
947                 }
948             
949                 // If within Document, child's subtree is informed that it's
950
// losing that status
951
lc=LCount.lookup(
952                              MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
953                 if(lc.captures+lc.bubbles+lc.defaults>0)
954                 {
955                     NodeImpl eventAncestor=this;
956                     if(enclosingAttr!=null)
957                         eventAncestor=
958                             (NodeImpl) enclosingAttr.node.getOwnerElement();
959                     if(eventAncestor!=null) // Might have been orphan Attr
960
{
961                         for(NodeImpl p=eventAncestor.parentNode();
962                             p!=null;
963                             p=p.parentNode())
964                         {
965                             eventAncestor=p; // Last non-null ancestor
966
}
967                         if(eventAncestor.getNodeType()==Node.DOCUMENT_NODE)
968                         {
969                             MutationEvent JavaDoc me= new MutationEventImpl();
970                             me.initMutationEvent(MutationEventImpl
971                                                .DOM_NODE_REMOVED_FROM_DOCUMENT,
972                                                  false,false,
973                                                  null,null,null,null,(short)0);
974                             dispatchEventToSubtree(oldInternal,me);
975                         }
976                     }
977                 }
978             }
979         } // End mutation preprocessing
980

981         // Patch linked list around oldChild
982
// Note: lastChild == firstChild.previousSibling
983
if (oldInternal == value) { // oldInternal == firstChild
984
// removing first child
985
oldInternal.isFirstChild(false);
986             value = oldInternal.nextSibling; // firstChild = oldInternal.nextSibling
987
ChildNode firstChild = (ChildNode) value;
988             if (firstChild != null) {
989                 firstChild.isFirstChild(true);
990                 firstChild.previousSibling = oldInternal.previousSibling;
991             }
992         } else {
993             ChildNode prev = oldInternal.previousSibling;
994             ChildNode next = oldInternal.nextSibling;
995             prev.nextSibling = next;
996             if (next == null) {
997                 // removing last child
998
ChildNode firstChild = (ChildNode) value;
999                 firstChild.previousSibling = prev;
1000            } else {
1001                // removing some other child in the middle
1002
next.previousSibling = prev;
1003            }
1004        }
1005
1006        // Save previous sibling for normalization checking.
1007
ChildNode oldPreviousSibling = oldInternal.previousSibling();
1008
1009        // Remove oldInternal's references to tree
1010
oldInternal.ownerNode = ownerDocument;
1011        oldInternal.isOwned(false);
1012        oldInternal.nextSibling = null;
1013        oldInternal.previousSibling = null;
1014
1015        changed();
1016
1017        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
1018        {
1019            // MUTATION POST-EVENTS:
1020
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
1021
// if required. (Common to most kinds of mutation)
1022
if( (mutationMask&MUTATION_AGGREGATE) != 0)
1023                dispatchAggregateEvents(enclosingAttr);
1024        } // End mutation postprocessing
1025

1026        checkNormalizationAfterRemove(oldPreviousSibling);
1027
1028        return oldInternal;
1029
1030    } // internalRemoveChild(Node,int):Node
1031

1032    /**
1033     * Make newChild occupy the location that oldChild used to
1034     * have. Note that newChild will first be removed from its previous
1035     * parent, if any. Equivalent to inserting newChild before oldChild,
1036     * then removing oldChild.
1037     *
1038     * @returns oldChild, in its new state (removed).
1039     *
1040     * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
1041     * type that shouldn't be a child of this node, or if newChild is
1042     * one of our ancestors.
1043     *
1044     * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
1045     * different owner document than we do.
1046     *
1047     * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
1048     * this node.
1049     *
1050     * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
1051     * read-only.
1052     */

1053    public Node replaceChild(Node newChild, Node oldChild)
1054        throws DOMException {
1055
1056        makeChildNode();
1057
1058        // If Mutation Events are being generated, this operation might
1059
// throw aggregate events twice when modifying an Attr -- once
1060
// on insertion and once on removal. DOM Level 2 does not specify
1061
// this as either desirable or undesirable, but hints that
1062
// aggregations should be issued only once per user request.
1063

1064        EnclosingAttr enclosingAttr=null;
1065        DocumentImpl ownerDocument = ownerDocument();
1066        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
1067        {
1068            // MUTATION PREPROCESSING AND PRE-EVENTS:
1069
// If we're within the scope of an Attr and DOMAttrModified
1070
// was requested, we need to preserve its previous value for
1071
// that event.
1072
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
1073            if(lc.captures+lc.bubbles+lc.defaults>0)
1074            {
1075                enclosingAttr=getEnclosingAttr();
1076            }
1077        } // End mutation preprocessing
1078

1079        internalInsertBefore(newChild, oldChild,MUTATION_LOCAL);
1080        if (newChild != oldChild) {
1081            internalRemoveChild(oldChild,MUTATION_LOCAL);
1082        }
1083
1084        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
1085        {
1086            dispatchAggregateEvents(enclosingAttr);
1087        }
1088
1089        return oldChild;
1090    }
1091
1092    //
1093
// NodeList methods
1094
//
1095

1096    /**
1097     * NodeList method: Count the immediate children of this node
1098     * @return int
1099     */

1100    public int getLength() {
1101
1102        if (hasStringValue()) {
1103            return 1;
1104        }
1105        ChildNode node = (ChildNode) value;
1106        int length = 0;
1107        for (; node != null; node = node.nextSibling) {
1108            length++;
1109        }
1110        return length;
1111
1112    } // getLength():int
1113

1114    /**
1115     * NodeList method: Return the Nth immediate child of this node, or
1116     * null if the index is out of bounds.
1117     * @return org.w3c.dom.Node
1118     * @param Index int
1119     */

1120    public Node item(int index) {
1121
1122        if (hasStringValue()) {
1123            if (index != 0 || value == null) {
1124                return null;
1125            }
1126            else {
1127                makeChildNode();
1128                return (Node) value;
1129            }
1130        }
1131        ChildNode node = (ChildNode) value;
1132        for (int i = 0; i < index && node != null; i++) {
1133            node = node.nextSibling;
1134        }
1135        return node;
1136
1137    } // item(int):Node
1138

1139    //
1140
// DOM2: methods, getters, setters
1141
//
1142

1143    //
1144
// Public methods
1145
//
1146

1147    /**
1148     * Override default behavior so that if deep is true, children are also
1149     * toggled.
1150     * @see Node
1151     * <P>
1152     * Note: this will not change the state of an EntityReference or its
1153     * children, which are always read-only.
1154     */

1155    public void setReadOnly(boolean readOnly, boolean deep) {
1156
1157        super.setReadOnly(readOnly, deep);
1158
1159        if (deep) {
1160
1161            if (needsSyncChildren()) {
1162                synchronizeChildren();
1163            }
1164
1165            if (hasStringValue()) {
1166                return;
1167            }
1168            // Recursively set kids
1169
for (ChildNode mykid = (ChildNode) value;
1170                 mykid != null;
1171                 mykid = mykid.nextSibling) {
1172                if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1173                    mykid.setReadOnly(readOnly,true);
1174                }
1175            }
1176        }
1177    } // setReadOnly(boolean,boolean)
1178

1179    //
1180
// Protected methods
1181
//
1182

1183    /**
1184     * Override this method in subclass to hook in efficient
1185     * internal data structure.
1186     */

1187    protected void synchronizeChildren() {
1188        // By default just change the flag to avoid calling this method again
1189
needsSyncChildren(false);
1190    }
1191
1192
1193    /**
1194     * Checks the normalized state of this node after inserting a child.
1195     * If the inserted child causes this node to be unnormalized, then this
1196     * node is flagged accordingly.
1197     * The conditions for changing the normalized state are:
1198     * <ul>
1199     * <li>The inserted child is a text node and one of its adjacent siblings
1200     * is also a text node.
1201     * <li>The inserted child is is itself unnormalized.
1202     * </ul>
1203     *
1204     * @param insertedChild the child node that was inserted into this node
1205     *
1206     * @throws NullPointerException if the inserted child is <code>null</code>
1207     */

1208    void checkNormalizationAfterInsert(ChildNode insertedChild) {
1209        // See if insertion caused this node to be unnormalized.
1210
if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1211            ChildNode prev = insertedChild.previousSibling();
1212            ChildNode next = insertedChild.nextSibling;
1213            // If an adjacent sibling of the new child is a text node,
1214
// flag this node as unnormalized.
1215
if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
1216                (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1217                isNormalized(false);
1218            }
1219        }
1220        else {
1221            // If the new child is not normalized,
1222
// then this node is inherently not normalized.
1223
if (!insertedChild.isNormalized()) {
1224                isNormalized(false);
1225            }
1226        }
1227    } // checkNormalizationAfterInsert(ChildNode)
1228

1229    /**
1230     * Checks the normalized of this node after removing a child.
1231     * If the removed child causes this node to be unnormalized, then this
1232     * node is flagged accordingly.
1233     * The conditions for changing the normalized state are:
1234     * <ul>
1235     * <li>The removed child had two adjacent siblings that were text nodes.
1236     * </ul>
1237     *
1238     * @param previousSibling the previous sibling of the removed child, or
1239     * <code>null</code>
1240     */

1241    void checkNormalizationAfterRemove(ChildNode previousSibling) {
1242        // See if removal caused this node to be unnormalized.
1243
// If the adjacent siblings of the removed child were both text nodes,
1244
// flag this node as unnormalized.
1245
if (previousSibling != null &&
1246            previousSibling.getNodeType() == Node.TEXT_NODE) {
1247
1248            ChildNode next = previousSibling.nextSibling;
1249            if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1250                isNormalized(false);
1251            }
1252        }
1253    } // checkNormalizationAfterRemove(ChildNode)
1254

1255    //
1256
// Serialization methods
1257
//
1258

1259    /** Serialize object. */
1260    private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
1261
1262        // synchronize chilren
1263
if (needsSyncChildren()) {
1264            synchronizeChildren();
1265        }
1266        // write object
1267
out.defaultWriteObject();
1268
1269    } // writeObject(ObjectOutputStream)
1270

1271    /** Deserialize object. */
1272    private void readObject(ObjectInputStream JavaDoc ois)
1273        throws ClassNotFoundException JavaDoc, IOException JavaDoc {
1274
1275        // perform default deseralization
1276
ois.defaultReadObject();
1277
1278        // hardset synchildren - so we don't try to sync- it does not make any sense
1279
// to try to synchildren when we just desealize object.
1280

1281        needsSyncChildren(false);
1282
1283    } // readObject(ObjectInputStream)
1284

1285} // class AttrImpl
1286
Popular Tags