View Javadoc

1   /*
2    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
3    * 
4    * This software is open source. 
5    * See the bottom of this file for the licence.
6    * 
7    * $Id: AbstractElement.java,v 1.77 2004/06/25 12:34:49 maartenc Exp $
8    */
9   
10  package org.dom4j.tree;
11  
12  import java.io.IOException;
13  import java.io.StringWriter;
14  import java.io.Writer;
15  import java.util.ArrayList;
16  import java.util.Collections;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.Map;
20  
21  import org.dom4j.Attribute;
22  import org.dom4j.CDATA;
23  import org.dom4j.CharacterData;
24  import org.dom4j.Comment;
25  import org.dom4j.Document;
26  import org.dom4j.DocumentFactory;
27  import org.dom4j.Element;
28  import org.dom4j.Entity;
29  import org.dom4j.IllegalAddException;
30  import org.dom4j.Namespace;
31  import org.dom4j.Node;
32  import org.dom4j.ProcessingInstruction;
33  import org.dom4j.QName;
34  import org.dom4j.Text;
35  import org.dom4j.Visitor;
36  import org.dom4j.io.XMLWriter;
37  import org.xml.sax.Attributes;
38  
39  /*** <p><code>AbstractElement</code> is an abstract base class for 
40    * tree implementors to use for implementation inheritence.</p>
41    *
42    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
43    * @version $Revision: 1.77 $
44    */
45  
46  public abstract class AbstractElement
47      extends AbstractBranch
48      implements Element {
49  
50      /*** The <code>DocumentFactory</code> instance used by default */
51  
52      private static final DocumentFactory DOCUMENT_FACTORY =
53          DocumentFactory.getInstance();
54  
55      protected static final List EMPTY_LIST = Collections.EMPTY_LIST;
56  
57      protected static final Iterator EMPTY_ITERATOR = EMPTY_LIST.iterator();
58  
59      protected static final boolean VERBOSE_TOSTRING = false;
60  
61      protected static final boolean USE_STRINGVALUE_SEPARATOR = false;
62  
63      public AbstractElement() {
64  
65      }
66  
67      public short getNodeType() {
68  
69          return ELEMENT_NODE;
70  
71      }
72  
73      public boolean isRootElement() {
74  
75          Document document = getDocument();
76  
77          if (document != null) {
78  
79              Element root = document.getRootElement();
80  
81              if (root == this) {
82  
83                  return true;
84  
85              }
86  
87          }
88  
89          return false;
90  
91      }
92  
93      public void setName(String name) {
94  
95          setQName(getDocumentFactory().createQName(name));
96  
97      }
98  
99      public void setNamespace(Namespace namespace) {
100 
101         setQName(getDocumentFactory().createQName(getName(), namespace));
102 
103     }
104 
105     /*** Returns the XPath expression to match this Elements name
106      * which is getQualifiedName() if there is a namespace prefix defined or
107      * if no namespace is present then it is getName() or if a namespace is defined
108      * with no prefix then the expression is *[name()='X'] where X = getName().    
109      */
110 
111     public String getXPathNameStep() {
112 
113         String uri = getNamespaceURI();
114 
115         if (uri == null || uri.length() == 0) {
116 
117             return getName();
118 
119         }
120 
121         String prefix = getNamespacePrefix();
122 
123         if (prefix == null || prefix.length() == 0) {
124 
125             return "*[name()='" + getName() + "']";
126 
127         }
128 
129         return getQualifiedName();
130 
131     }
132 
133     public String getPath(Element context) {
134         
135         if (this == context) {
136             return ".";
137         }
138 
139         Element parent = getParent();
140 
141         if (parent == null) {
142 
143             return "/" + getXPathNameStep();
144 
145         }
146 
147         else if (parent == context) {
148 
149             return getXPathNameStep();
150 
151         }
152 
153         return parent.getPath(context) + "/" + getXPathNameStep();
154 
155     }
156 
157     public String getUniquePath(Element context) {
158 
159         Element parent = getParent();
160 
161         if (parent == null) {
162 
163             return "/" + getXPathNameStep();
164 
165         }
166 
167         StringBuffer buffer = new StringBuffer();
168 
169         if (parent != context) {
170 
171             buffer.append(parent.getUniquePath(context));
172 
173             buffer.append("/");
174 
175         }
176 
177         buffer.append(getXPathNameStep());
178 
179         List mySiblings = parent.elements(getQName());
180 
181         if (mySiblings.size() > 1) {
182 
183             int idx = mySiblings.indexOf(this);
184 
185             if (idx >= 0) {
186 
187                 buffer.append("[");
188 
189                 buffer.append(Integer.toString(++idx));
190 
191                 buffer.append("]");
192 
193             }
194 
195         }
196 
197         return buffer.toString();
198 
199     }
200 
201     public String asXML() {
202 
203         try {
204 
205             StringWriter out = new StringWriter();
206 
207             XMLWriter writer = new XMLWriter(out, outputFormat);
208 
209             writer.write(this);
210 
211             return out.toString();
212 
213         }
214 
215         catch (IOException e) {
216 
217             throw new RuntimeException(
218                 "Wierd IOException while generating textual representation: " + e.getMessage());
219 
220         }
221 
222     }
223 
224     public void write(Writer out) throws IOException {
225 
226         XMLWriter writer = new XMLWriter(out, outputFormat);
227 
228         writer.write(this);
229 
230     }
231 
232     /*** <p><code>accept</code> method is the <code>Visitor Pattern</code> method.
233       * </p>
234       *
235       * @param visitor <code>Visitor</code> is the visitor.
236       */
237 
238     public void accept(Visitor visitor) {
239 
240         visitor.visit(this);
241 
242         // visit attributes
243 
244         for (int i = 0, size = attributeCount(); i < size; i++) {
245 
246             Attribute attribute = attribute(i);
247 
248             visitor.visit(attribute);
249 
250         }
251 
252         // visit content
253 
254         for (int i = 0, size = nodeCount(); i < size; i++) {
255 
256             Node node = node(i);
257 
258             node.accept(visitor);
259 
260         }
261 
262     }
263 
264     public String toString() {
265 
266         String uri = getNamespaceURI();
267 
268         if (uri != null && uri.length() > 0) {
269 
270             if (VERBOSE_TOSTRING) {
271 
272                 return super.toString()
273                     + " [Element: <"
274                     + getQualifiedName()
275                     + " uri: "
276                     + uri
277                     + " attributes: "
278                     + attributeList()
279                     + " content: "
280                     + contentList()
281                     + " />]";
282 
283             }
284 
285             else {
286 
287                 return super.toString()
288                     + " [Element: <"
289                     + getQualifiedName()
290                     + " uri: "
291                     + uri
292                     + " attributes: "
293                     + attributeList()
294                     + "/>]";
295 
296             }
297 
298         }
299 
300         else {
301 
302             if (VERBOSE_TOSTRING) {
303 
304                 return super.toString()
305                     + " [Element: <"
306                     + getQualifiedName()
307                     + " attributes: "
308                     + attributeList()
309                     + " content: "
310                     + contentList()
311                     + " />]";
312 
313             }
314 
315             else {
316 
317                 return super.toString()
318                     + " [Element: <"
319                     + getQualifiedName()
320                     + " attributes: "
321                     + attributeList()
322                     + "/>]";
323 
324             }
325 
326         }
327 
328     }
329 
330     // QName methods
331 
332     //-------------------------------------------------------------------------    
333 
334     public Namespace getNamespace() {
335 
336         return getQName().getNamespace();
337 
338     }
339 
340     public String getName() {
341 
342         return getQName().getName();
343 
344     }
345 
346     public String getNamespacePrefix() {
347 
348         return getQName().getNamespacePrefix();
349 
350     }
351 
352     public String getNamespaceURI() {
353 
354         return getQName().getNamespaceURI();
355 
356     }
357 
358     public String getQualifiedName() {
359 
360         return getQName().getQualifiedName();
361 
362     }
363 
364     public Object getData() {
365 
366         return getText();
367 
368     }
369 
370     public void setData(Object data) {
371 
372         // ignore this method
373 
374     }
375 
376     // Node methods
377 
378     //-------------------------------------------------------------------------    
379 
380     public Node node(int index) {
381 
382         if (index >= 0) {
383 
384             List list = contentList();
385 
386             if (index >= list.size()) {
387 
388                 return null;
389 
390             }
391 
392             Object node = list.get(index);
393 
394             if (node != null) {
395 
396                 if (node instanceof Node) {
397 
398                     return (Node) node;
399 
400                 }
401 
402                 else {
403 
404                     return getDocumentFactory().createText(node.toString());
405 
406                 }
407 
408             }
409 
410         }
411 
412         return null;
413 
414     }
415 
416     public int indexOf(Node node) {
417 
418         return contentList().indexOf(node);
419 
420     }
421 
422     public int nodeCount() {
423 
424         return contentList().size();
425 
426     }
427 
428     public Iterator nodeIterator() {
429 
430         return contentList().iterator();
431 
432     }
433 
434     // Element methods
435 
436     //-------------------------------------------------------------------------    
437 
438     public Element element(String name) {
439 
440         List list = contentList();
441 
442         int size = list.size();
443 
444         for (int i = 0; i < size; i++) {
445 
446             Object object = list.get(i);
447 
448             if (object instanceof Element) {
449 
450                 Element element = (Element) object;
451 
452                 if (name.equals(element.getName())) {
453 
454                     return element;
455 
456                 }
457 
458             }
459 
460         }
461 
462         return null;
463 
464     }
465 
466     public Element element(QName qName) {
467 
468         List list = contentList();
469 
470         int size = list.size();
471 
472         for (int i = 0; i < size; i++) {
473 
474             Object object = list.get(i);
475 
476             if (object instanceof Element) {
477 
478                 Element element = (Element) object;
479 
480                 if (qName.equals(element.getQName())) {
481 
482                     return element;
483 
484                 }
485 
486             }
487 
488         }
489 
490         return null;
491 
492     }
493 
494     public Element element(String name, Namespace namespace) {
495 
496         return element(getDocumentFactory().createQName(name, namespace));
497 
498     }
499 
500     public List elements() {
501 
502         List list = contentList();
503 
504         BackedList answer = createResultList();
505 
506         int size = list.size();
507 
508         for (int i = 0; i < size; i++) {
509 
510             Object object = list.get(i);
511 
512             if (object instanceof Element) {
513 
514                 answer.addLocal(object);
515 
516             }
517 
518         }
519 
520         return answer;
521 
522     }
523 
524     public List elements(String name) {
525 
526         List list = contentList();
527 
528         BackedList answer = createResultList();
529 
530         int size = list.size();
531 
532         for (int i = 0; i < size; i++) {
533 
534             Object object = list.get(i);
535 
536             if (object instanceof Element) {
537 
538                 Element element = (Element) object;
539 
540                 if (name.equals(element.getName())) {
541 
542                     answer.addLocal(element);
543 
544                 }
545 
546             }
547 
548         }
549 
550         return answer;
551 
552     }
553 
554     public List elements(QName qName) {
555 
556         List list = contentList();
557 
558         BackedList answer = createResultList();
559 
560         int size = list.size();
561 
562         for (int i = 0; i < size; i++) {
563 
564             Object object = list.get(i);
565 
566             if (object instanceof Element) {
567 
568                 Element element = (Element) object;
569 
570                 if (qName.equals(element.getQName())) {
571 
572                     answer.addLocal(element);
573 
574                 }
575 
576             }
577 
578         }
579 
580         return answer;
581 
582     }
583 
584     public List elements(String name, Namespace namespace) {
585 
586         return elements(getDocumentFactory().createQName(name, namespace));
587 
588     }
589 
590     public Iterator elementIterator() {
591 
592         List list = elements();
593 
594         return list.iterator();
595 
596     }
597 
598     public Iterator elementIterator(String name) {
599 
600         List list = elements(name);
601 
602         return list.iterator();
603 
604     }
605 
606     public Iterator elementIterator(QName qName) {
607 
608         List list = elements(qName);
609 
610         return list.iterator();
611 
612     }
613 
614     public Iterator elementIterator(String name, Namespace namespace) {
615 
616         return elementIterator(getDocumentFactory().createQName(name, namespace));
617 
618     }
619 
620     // Attribute methods
621 
622     //-------------------------------------------------------------------------    
623 
624     public List attributes() {
625 
626         return new ContentListFacade(this, attributeList());
627 
628     }
629 
630     public Iterator attributeIterator() {
631 
632         return attributeList().iterator();
633 
634     }
635 
636     public Attribute attribute(int index) {
637 
638         return (Attribute) attributeList().get(index);
639 
640     }
641 
642     public int attributeCount() {
643 
644         return attributeList().size();
645 
646     }
647 
648     public Attribute attribute(String name) {
649 
650         List list = attributeList();
651 
652         int size = list.size();
653 
654         for (int i = 0; i < size; i++) {
655 
656             Attribute attribute = (Attribute) list.get(i);
657 
658             if (name.equals(attribute.getName())) {
659 
660                 return attribute;
661 
662             }
663 
664         }
665 
666         return null;
667 
668     }
669 
670     public Attribute attribute(QName qName) {
671 
672         List list = attributeList();
673 
674         int size = list.size();
675 
676         for (int i = 0; i < size; i++) {
677 
678             Attribute attribute = (Attribute) list.get(i);
679 
680             if (qName.equals(attribute.getQName())) {
681 
682                 return attribute;
683 
684             }
685 
686         }
687 
688         return null;
689 
690     }
691 
692     public Attribute attribute(String name, Namespace namespace) {
693 
694         return attribute(getDocumentFactory().createQName(name, namespace));
695 
696     }
697 
698     /*** This method provides a more optimal way of setting all the attributes
699      * on an Element particularly for use in {@link org.dom4j.io.SAXReader}.
700       */
701 
702     public void setAttributes(
703         Attributes attributes,
704         NamespaceStack namespaceStack,
705         boolean noNamespaceAttributes) {
706 
707         // now lets add all attribute values
708 
709         int size = attributes.getLength();
710 
711         if (size > 0) {
712 
713             DocumentFactory factory = getDocumentFactory();
714 
715             if (size == 1) {
716 
717                 // allow lazy construction of the List of Attributes
718 
719                 String attributeQualifiedName = attributes.getQName(0);
720 
721                 if (noNamespaceAttributes || !attributeQualifiedName.startsWith("xmlns")) {
722 
723                     String attributeURI = attributes.getURI(0);
724 
725                     String attributeLocalName = attributes.getLocalName(0);
726 
727                     String attributeValue = attributes.getValue(0);
728 
729                     QName attributeQName =
730                         namespaceStack.getAttributeQName(
731                             attributeURI,
732                             attributeLocalName,
733                             attributeQualifiedName);
734 
735                     add(factory.createAttribute(this, attributeQName, attributeValue));
736 
737                 }
738 
739             }
740 
741             else {
742 
743                 List list = attributeList(size);
744 
745                 list.clear();
746 
747                 for (int i = 0; i < size; i++) {
748 
749                     // optimised to avoid the call to attribute(QName) to 
750 
751                     // lookup an attribute for a given QName
752 
753                     String attributeQualifiedName = attributes.getQName(i);
754 
755                     if (noNamespaceAttributes || !attributeQualifiedName.startsWith("xmlns")) {
756 
757                         String attributeURI = attributes.getURI(i);
758 
759                         String attributeLocalName = attributes.getLocalName(i);
760 
761                         String attributeValue = attributes.getValue(i);
762 
763                         QName attributeQName =
764                             namespaceStack.getAttributeQName(
765                                 attributeURI,
766                                 attributeLocalName,
767                                 attributeQualifiedName);
768 
769                         Attribute attribute =
770                             factory.createAttribute(this, attributeQName, attributeValue);
771 
772                         list.add(attribute);
773 
774                         childAdded(attribute);
775 
776                     }
777 
778                 }
779 
780             }
781 
782         }
783 
784     }
785 
786     public String attributeValue(String name) {
787 
788         Attribute attrib = attribute(name);
789 
790         if (attrib == null) {
791 
792             return null;
793 
794         }
795 
796         else {
797 
798             return attrib.getValue();
799 
800         }
801 
802     }
803 
804     public String attributeValue(QName qName) {
805 
806         Attribute attrib = attribute(qName);
807 
808         if (attrib == null) {
809 
810             return null;
811 
812         }
813 
814         else {
815 
816             return attrib.getValue();
817 
818         }
819 
820     }
821 
822     public String attributeValue(String name, String defaultValue) {
823 
824         String answer = attributeValue(name);
825 
826         return (answer != null) ? answer : defaultValue;
827 
828     }
829 
830     public String attributeValue(QName qName, String defaultValue) {
831 
832         String answer = attributeValue(qName);
833 
834         return (answer != null) ? answer : defaultValue;
835 
836     }
837 
838     /***
839      * @deprecated As of version 0.5. Please use 
840      *    {@link #addAttribute(String,String)} instead.
841      *    WILL BE REMOVED IN dom4j-1.6 !!
842      **/
843     public void setAttributeValue(String name, String value) {
844 
845         addAttribute(name, value);
846 
847     }
848 
849     /***
850      * @deprecated As of version 0.5. Please use 
851      *    {@link #addAttribute(String,String)} instead.
852      *    WILL BE REMOVED IN dom4j-1.6 !!
853      **/
854     public void setAttributeValue(QName qName, String value) {
855 
856         addAttribute(qName, value);
857 
858     }
859 
860     public void add(Attribute attribute) {
861 
862         if (attribute.getParent() != null) {
863 
864             String message =
865                 "The Attribute already has an existing parent \""
866                     + attribute.getParent().getQualifiedName()
867                     + "\"";
868 
869             throw new IllegalAddException(this, attribute, message);
870 
871         }
872 
873         if (attribute.getValue() == null) {
874 
875             // try remove a previous attribute with the same
876 
877             // name since adding an attribute with a null value
878 
879             // is equivalent to removing it.
880 
881             Attribute oldAttribute = attribute(attribute.getQName());
882 
883             if (oldAttribute != null) {
884 
885                 remove(oldAttribute);
886 
887             }
888 
889         }
890 
891         else {
892 
893             attributeList().add(attribute);
894 
895             childAdded(attribute);
896 
897         }
898 
899     }
900 
901     public boolean remove(Attribute attribute) {
902 
903         List list = attributeList();
904 
905         boolean answer = list.remove(attribute);
906 
907         if (answer) {
908 
909             childRemoved(attribute);
910 
911         }
912 
913         else {
914 
915             // we may have a copy of the attribute
916 
917             Attribute copy = attribute(attribute.getQName());
918 
919             if (copy != null) {
920 
921                 list.remove(copy);
922 
923                 answer = true;
924 
925             }
926 
927         }
928 
929         return answer;
930 
931     }
932 
933     // Processing instruction API
934 
935     //-------------------------------------------------------------------------    
936 
937     public List processingInstructions() {
938 
939         List list = contentList();
940 
941         BackedList answer = createResultList();
942 
943         int size = list.size();
944 
945         for (int i = 0; i < size; i++) {
946 
947             Object object = list.get(i);
948 
949             if (object instanceof ProcessingInstruction) {
950 
951                 answer.addLocal(object);
952 
953             }
954 
955         }
956 
957         return answer;
958 
959     }
960 
961     public List processingInstructions(String target) {
962 
963         List list = contentList();
964 
965         BackedList answer = createResultList();
966 
967         int size = list.size();
968 
969         for (int i = 0; i < size; i++) {
970 
971             Object object = list.get(i);
972 
973             if (object instanceof ProcessingInstruction) {
974 
975                 ProcessingInstruction pi = (ProcessingInstruction) object;
976 
977                 if (target.equals(pi.getName())) {
978 
979                     answer.addLocal(pi);
980 
981                 }
982 
983             }
984 
985         }
986 
987         return answer;
988 
989     }
990 
991     public ProcessingInstruction processingInstruction(String target) {
992 
993         List list = contentList();
994 
995         int size = list.size();
996 
997         for (int i = 0; i < size; i++) {
998 
999             Object object = list.get(i);
1000 
1001             if (object instanceof ProcessingInstruction) {
1002 
1003                 ProcessingInstruction pi = (ProcessingInstruction) object;
1004 
1005                 if (target.equals(pi.getName())) {
1006 
1007                     return pi;
1008 
1009                 }
1010 
1011             }
1012 
1013         }
1014 
1015         return null;
1016 
1017     }
1018 
1019     public boolean removeProcessingInstruction(String target) {
1020 
1021         List list = contentList();
1022 
1023         for (Iterator iter = list.iterator(); iter.hasNext();) {
1024 
1025             Object object = iter.next();
1026 
1027             if (object instanceof ProcessingInstruction) {
1028 
1029                 ProcessingInstruction pi = (ProcessingInstruction) object;
1030 
1031                 if (target.equals(pi.getName())) {
1032 
1033                     iter.remove();
1034 
1035                     return true;
1036 
1037                 }
1038 
1039             }
1040 
1041         }
1042 
1043         return false;
1044 
1045     }
1046 
1047     // Content Model methods
1048 
1049     //-------------------------------------------------------------------------    
1050 
1051     public Node getXPathResult(int index) {
1052 
1053         Node answer = node(index);
1054 
1055         if (answer != null && !answer.supportsParent()) {
1056 
1057             return answer.asXPathResult(this);
1058 
1059         }
1060 
1061         return answer;
1062 
1063     }
1064 
1065     public Element addAttribute(String name, String value) {
1066 
1067         // adding a null value is equivalent to removing the attribute
1068 
1069         Attribute attribute = attribute(name);
1070 
1071         if (value != null) {
1072 
1073             if (attribute == null) {
1074 
1075                 add(getDocumentFactory().createAttribute(this, name, value));
1076 
1077             }
1078 
1079             else if (attribute.isReadOnly()) {
1080 
1081                 remove(attribute);
1082 
1083                 add(getDocumentFactory().createAttribute(this, name, value));
1084 
1085             }
1086 
1087             else {
1088 
1089                 attribute.setValue(value);
1090 
1091             }
1092 
1093         }
1094 
1095         else if (attribute != null) {
1096 
1097             remove(attribute);
1098 
1099         }
1100 
1101         return this;
1102 
1103     }
1104 
1105     public Element addAttribute(QName qName, String value) {
1106 
1107         // adding a null value is equivalent to removing the attribute
1108 
1109         Attribute attribute = attribute(qName);
1110 
1111         if (value != null) {
1112 
1113             if (attribute == null) {
1114 
1115                 add(getDocumentFactory().createAttribute(this, qName, value));
1116 
1117             }
1118 
1119             else if (attribute.isReadOnly()) {
1120 
1121                 remove(attribute);
1122 
1123                 add(getDocumentFactory().createAttribute(this, qName, value));
1124 
1125             }
1126 
1127             else {
1128 
1129                 attribute.setValue(value);
1130 
1131             }
1132 
1133         }
1134 
1135         else if (attribute != null) {
1136 
1137             remove(attribute);
1138 
1139         }
1140 
1141         return this;
1142 
1143     }
1144 
1145     public Element addCDATA(String cdata) {
1146 
1147         CDATA node = getDocumentFactory().createCDATA(cdata);
1148 
1149         addNewNode(node);
1150 
1151         return this;
1152 
1153     }
1154 
1155     public Element addComment(String comment) {
1156 
1157         Comment node = getDocumentFactory().createComment(comment);
1158 
1159         addNewNode(node);
1160 
1161         return this;
1162 
1163     }
1164 
1165     public Element addElement(String name) {
1166 
1167         DocumentFactory factory = getDocumentFactory();
1168 
1169         int index = name.indexOf(":");
1170 
1171         String prefix = "";
1172 
1173         String localName = name;
1174 
1175         Namespace namespace = null;
1176 
1177         if (index > 0) {
1178 
1179             prefix = name.substring(0, index);
1180 
1181             localName = name.substring(index + 1);
1182 
1183             namespace = getNamespaceForPrefix(prefix);
1184 
1185             if (namespace == null) {
1186 
1187                 throw new IllegalAddException(
1188                     "No such namespace prefix: "
1189                         + prefix
1190                         + " is in scope on: "
1191                         + this
1192                         + " so cannot add element: "
1193                         + name);
1194 
1195             }
1196 
1197         }
1198 
1199         else {
1200 
1201             namespace = getNamespaceForPrefix("");
1202 
1203         }
1204 
1205         Element node;
1206 
1207         if (namespace != null) {
1208 
1209             QName qname = factory.createQName(localName, namespace);
1210 
1211             node = factory.createElement(qname);
1212 
1213         }
1214 
1215         else {
1216 
1217             node = factory.createElement(name);
1218 
1219         }
1220 
1221         addNewNode(node);
1222 
1223         return node;
1224 
1225     }
1226 
1227     public Element addEntity(String name, String text) {
1228 
1229         Entity node = getDocumentFactory().createEntity(name, text);
1230 
1231         addNewNode(node);
1232 
1233         return this;
1234 
1235     }
1236 
1237     public Element addNamespace(String prefix, String uri) {
1238 
1239         Namespace node = getDocumentFactory().createNamespace(prefix, uri);
1240 
1241         addNewNode(node);
1242 
1243         return this;
1244 
1245     }
1246 
1247     public Element addProcessingInstruction(String target, String data) {
1248 
1249         ProcessingInstruction node =
1250             getDocumentFactory().createProcessingInstruction(target, data);
1251 
1252         addNewNode(node);
1253 
1254         return this;
1255 
1256     }
1257 
1258     public Element addProcessingInstruction(String target, Map data) {
1259 
1260         ProcessingInstruction node =
1261             getDocumentFactory().createProcessingInstruction(target, data);
1262 
1263         addNewNode(node);
1264 
1265         return this;
1266 
1267     }
1268 
1269     public Element addText(String text) {
1270 
1271         Text node = getDocumentFactory().createText(text);
1272 
1273         addNewNode(node);
1274 
1275         return this;
1276 
1277     }
1278 
1279     // polymorphic node methods    
1280 
1281     public void add(Node node) {
1282 
1283         switch (node.getNodeType()) {
1284 
1285             case ELEMENT_NODE :
1286 
1287                 add((Element) node);
1288 
1289                 break;
1290 
1291             case ATTRIBUTE_NODE :
1292 
1293                 add((Attribute) node);
1294 
1295                 break;
1296 
1297             case TEXT_NODE :
1298 
1299                 add((Text) node);
1300 
1301                 break;
1302 
1303             case CDATA_SECTION_NODE :
1304 
1305                 add((CDATA) node);
1306 
1307                 break;
1308 
1309             case ENTITY_REFERENCE_NODE :
1310 
1311                 add((Entity) node);
1312 
1313                 break;
1314 
1315             case PROCESSING_INSTRUCTION_NODE :
1316 
1317                 add((ProcessingInstruction) node);
1318 
1319                 break;
1320 
1321             case COMMENT_NODE :
1322 
1323                 add((Comment) node);
1324 
1325                 break;
1326 
1327                 /*  XXXX: to do!              
1328                             case DOCUMENT_TYPE_NODE:
1329                                 add((DocumentType) node);
1330                                 break;
1331                 */
1332 
1333             case NAMESPACE_NODE :
1334 
1335                 add((Namespace) node);
1336 
1337                 break;
1338 
1339             default :
1340 
1341                 invalidNodeTypeAddException(node);
1342 
1343         }
1344 
1345     }
1346 
1347     public boolean remove(Node node) {
1348 
1349         switch (node.getNodeType()) {
1350 
1351             case ELEMENT_NODE :
1352 
1353                 return remove((Element) node);
1354 
1355             case ATTRIBUTE_NODE :
1356 
1357                 return remove((Attribute) node);
1358 
1359             case TEXT_NODE :
1360 
1361                 return remove((Text) node);
1362 
1363             case CDATA_SECTION_NODE :
1364 
1365                 return remove((CDATA) node);
1366 
1367             case ENTITY_REFERENCE_NODE :
1368 
1369                 return remove((Entity) node);
1370 
1371             case PROCESSING_INSTRUCTION_NODE :
1372 
1373                 return remove((ProcessingInstruction) node);
1374 
1375             case COMMENT_NODE :
1376 
1377                 return remove((Comment) node);
1378 
1379                 /*                
1380                             case DOCUMENT_TYPE_NODE:
1381                                 return remove((DocumentType) node);
1382                 */
1383 
1384             case NAMESPACE_NODE :
1385 
1386                 return remove((Namespace) node);
1387 
1388             default :
1389 
1390                 return false;
1391 
1392         }
1393 
1394     }
1395 
1396     // typesafe versions using node classes
1397 
1398     public void add(CDATA cdata) {
1399 
1400         addNode(cdata);
1401 
1402     }
1403 
1404     public void add(Comment comment) {
1405 
1406         addNode(comment);
1407 
1408     }
1409 
1410     public void add(Element element) {
1411 
1412         addNode(element);
1413 
1414     }
1415 
1416     public void add(Entity entity) {
1417 
1418         addNode(entity);
1419 
1420     }
1421 
1422     public void add(Namespace namespace) {
1423 
1424         addNode(namespace);
1425 
1426     }
1427 
1428     public void add(ProcessingInstruction pi) {
1429 
1430         addNode(pi);
1431 
1432     }
1433 
1434     public void add(Text text) {
1435 
1436         addNode(text);
1437 
1438     }
1439 
1440     public boolean remove(CDATA cdata) {
1441 
1442         return removeNode(cdata);
1443 
1444     }
1445 
1446     public boolean remove(Comment comment) {
1447 
1448         return removeNode(comment);
1449 
1450     }
1451 
1452     public boolean remove(Element element) {
1453 
1454         return removeNode(element);
1455 
1456     }
1457 
1458     public boolean remove(Entity entity) {
1459 
1460         return removeNode(entity);
1461 
1462     }
1463 
1464     public boolean remove(Namespace namespace) {
1465 
1466         return removeNode(namespace);
1467 
1468     }
1469 
1470     public boolean remove(ProcessingInstruction pi) {
1471 
1472         return removeNode(pi);
1473 
1474     }
1475 
1476     public boolean remove(Text text) {
1477 
1478         return removeNode(text);
1479 
1480     }
1481 
1482     // Helper methods
1483 
1484     //-------------------------------------------------------------------------    
1485 
1486     public boolean hasMixedContent() {
1487 
1488         List content = contentList();
1489 
1490         if (content == null || content.isEmpty() || content.size() < 2) {
1491 
1492             return false;
1493 
1494         }
1495 
1496         Class prevClass = null;
1497 
1498         for (Iterator iter = content.iterator(); iter.hasNext();) {
1499 
1500             Object object = iter.next();
1501 
1502             Class newClass = object.getClass();
1503 
1504             if (newClass != prevClass) {
1505 
1506                 if (prevClass != null) {
1507 
1508                     return true;
1509 
1510                 }
1511 
1512                 prevClass = newClass;
1513 
1514             }
1515 
1516         }
1517 
1518         return false;
1519 
1520     }
1521 
1522     public boolean isTextOnly() {
1523 
1524         List content = contentList();
1525 
1526         if (content == null || content.isEmpty()) {
1527 
1528             return true;
1529 
1530         }
1531 
1532         for (Iterator iter = content.iterator(); iter.hasNext();) {
1533 
1534             Object object = iter.next();
1535 
1536             if (!(object instanceof CharacterData) && !(object instanceof String)) {
1537 
1538                 return false;
1539 
1540             }
1541 
1542         }
1543 
1544         return true;
1545 
1546     }
1547 
1548     public void setText(String text) {
1549 
1550         /* remove all text nodes */
1551         List allContent = contentList();
1552         if (allContent != null) {
1553             Iterator it = allContent.iterator();
1554             while (it.hasNext()) {
1555                 Node node = (Node) it.next();
1556                 switch (node.getNodeType()) {
1557                     case CDATA_SECTION_NODE:
1558                     //case ENTITY_NODE:
1559                     case ENTITY_REFERENCE_NODE:
1560                     case TEXT_NODE:
1561                         it.remove();
1562                 }
1563             }
1564         }
1565 
1566         addText(text);
1567 
1568     }
1569 
1570     public String getStringValue() {
1571 
1572         List list = contentList();
1573 
1574         int size = list.size();
1575 
1576         if (size > 0) {
1577 
1578             if (size == 1) {
1579 
1580                 // optimised to avoid StringBuffer creation
1581 
1582                 return getContentAsStringValue(list.get(0));
1583 
1584             }
1585 
1586             else {
1587 
1588                 StringBuffer buffer = new StringBuffer();
1589 
1590                 for (int i = 0; i < size; i++) {
1591 
1592                     Object node = list.get(i);
1593 
1594                     String string = getContentAsStringValue(node);
1595 
1596                     if (string.length() > 0) {
1597 
1598                         if (USE_STRINGVALUE_SEPARATOR) {
1599 
1600                             if (buffer.length() > 0) {
1601 
1602                                 buffer.append(' ');
1603 
1604                             }
1605 
1606                         }
1607 
1608                         buffer.append(string);
1609 
1610                     }
1611 
1612                 }
1613 
1614                 return buffer.toString();
1615 
1616             }
1617 
1618         }
1619 
1620         return "";
1621 
1622     }
1623 
1624     /***
1625      * Puts all <code>Text</code> nodes in the full depth of the sub-tree 
1626      * underneath this <code>Node</code>, including attribute nodes, into a 
1627      * "normal" form where only structure (e.g., elements, comments, 
1628      * processing instructions, CDATA sections, and entity references) 
1629      * separates <code>Text</code> nodes, i.e., there are neither adjacent 
1630      * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can 
1631      * be used to ensure that the DOM view of a document is the same as if 
1632      * it were saved and re-loaded, and is useful when operations (such as 
1633      * XPointer  lookups) that depend on a particular document tree 
1634      * structure are to be used.In cases where the document contains 
1635      * <code>CDATASections</code>, the normalize operation alone may not be 
1636      * sufficient, since XPointers do not differentiate between 
1637      * <code>Text</code> nodes and <code>CDATASection</code> nodes.
1638      * @since DOM Level 2
1639      */
1640 
1641     public void normalize() {
1642 
1643         List content = contentList();
1644 
1645         Text previousText = null;
1646 
1647         int i = 0;
1648 
1649         while (i < content.size()) {
1650 
1651             Node node = (Node) content.get(i);
1652 
1653             if (node instanceof Text) {
1654 
1655                 Text text = (Text) node;
1656 
1657                 if (previousText != null) {
1658 
1659                     previousText.appendText(text.getText());
1660 
1661                     remove(text);
1662 
1663                 }
1664 
1665                 else {
1666 
1667                     String value = text.getText();
1668 
1669                     // only remove empty Text nodes, not whitespace nodes
1670 
1671                     //if ( value == null || value.trim().length() <= 0 ) {
1672 
1673                     if (value == null || value.length() <= 0) {
1674 
1675                         remove(text);
1676 
1677                     }
1678 
1679                     else {
1680 
1681                         previousText = text;
1682 
1683                         i++;
1684 
1685                     }
1686 
1687                 }
1688 
1689             }
1690 
1691             else {
1692 
1693                 if (node instanceof Element) {
1694 
1695                     Element element = (Element) node;
1696 
1697                     element.normalize();
1698 
1699                 }
1700 
1701                 previousText = null;
1702 
1703                 i++;
1704 
1705             }
1706 
1707         }
1708 
1709     }
1710 
1711     public String elementText(String name) {
1712 
1713         Element element = element(name);
1714 
1715         return (element != null) ? element.getText() : null;
1716 
1717     }
1718 
1719     public String elementText(QName qName) {
1720 
1721         Element element = element(qName);
1722 
1723         return (element != null) ? element.getText() : null;
1724 
1725     }
1726 
1727     public String elementTextTrim(String name) {
1728 
1729         Element element = element(name);
1730 
1731         return (element != null) ? element.getTextTrim() : null;
1732 
1733     }
1734 
1735     public String elementTextTrim(QName qName) {
1736 
1737         Element element = element(qName);
1738 
1739         return (element != null) ? element.getTextTrim() : null;
1740 
1741     }
1742 
1743     // add to me content from another element
1744 
1745     // analagous to the addAll(collection) methods in Java 2 collections
1746 
1747     public void appendAttributes(Element element) {
1748 
1749         for (int i = 0, size = element.attributeCount(); i < size; i++) {
1750 
1751             Attribute attribute = element.attribute(i);
1752 
1753             if (attribute.supportsParent()) {
1754 
1755                 addAttribute(attribute.getQName(), attribute.getValue());
1756 
1757             }
1758 
1759             else {
1760 
1761                 add(attribute);
1762 
1763             }
1764 
1765         }
1766 
1767     }
1768 
1769     /*** <p>This returns a deep clone of this element.
1770       * The new element is detached from its parent, and getParent() on the 
1771       * clone will return null.</p>
1772       *
1773       * @return the clone of this element
1774       */
1775 
1776     /*    
1777         public Object clone() {
1778             Element clone = createElement(getQName());
1779             clone.appendAttributes(this);
1780             clone.appendContent(this);
1781             return clone;
1782         }
1783     */
1784 
1785     public Element createCopy() {
1786 
1787         Element clone = createElement(getQName());
1788 
1789         clone.appendAttributes(this);
1790 
1791         clone.appendContent(this);
1792 
1793         return clone;
1794 
1795     }
1796 
1797     public Element createCopy(String name) {
1798 
1799         Element clone = createElement(name);
1800 
1801         clone.appendAttributes(this);
1802 
1803         clone.appendContent(this);
1804 
1805         return clone;
1806 
1807     }
1808 
1809     public Element createCopy(QName qName) {
1810 
1811         Element clone = createElement(qName);
1812 
1813         clone.appendAttributes(this);
1814 
1815         clone.appendContent(this);
1816 
1817         return clone;
1818 
1819     }
1820 
1821     public QName getQName(String qualifiedName) {
1822 
1823         String prefix = "";
1824 
1825         String localName = qualifiedName;
1826 
1827         int index = qualifiedName.indexOf(":");
1828 
1829         if (index > 0) {
1830 
1831             prefix = qualifiedName.substring(0, index);
1832 
1833             localName = qualifiedName.substring(index + 1);
1834 
1835         }
1836 
1837         Namespace namespace = getNamespaceForPrefix(prefix);
1838 
1839         if (namespace != null) {
1840 
1841             return getDocumentFactory().createQName(localName, namespace);
1842 
1843         }
1844 
1845         else {
1846 
1847             return getDocumentFactory().createQName(localName);
1848 
1849         }
1850 
1851     }
1852 
1853     public Namespace getNamespaceForPrefix(String prefix) {
1854 
1855         if (prefix == null) {
1856 
1857             prefix = "";
1858 
1859         }
1860 
1861         if (prefix.equals(getNamespacePrefix())) {
1862 
1863             return getNamespace();
1864 
1865         }
1866 
1867         else if (prefix.equals("xml")) {
1868 
1869             return Namespace.XML_NAMESPACE;
1870 
1871         }
1872 
1873         else {
1874 
1875             List list = contentList();
1876 
1877             int size = list.size();
1878 
1879             for (int i = 0; i < size; i++) {
1880 
1881                 Object object = list.get(i);
1882 
1883                 if (object instanceof Namespace) {
1884 
1885                     Namespace namespace = (Namespace) object;
1886 
1887                     if (prefix.equals(namespace.getPrefix())) {
1888 
1889                         return namespace;
1890 
1891                     }
1892 
1893                 }
1894 
1895             }
1896 
1897         }
1898 
1899         Element parent = getParent();
1900 
1901         if (parent != null) {
1902 
1903             Namespace answer = parent.getNamespaceForPrefix(prefix);
1904 
1905             if (answer != null) {
1906 
1907                 return answer;
1908 
1909             }
1910 
1911         }
1912 
1913         if (prefix == null || prefix.length() <= 0) {
1914 
1915             return Namespace.NO_NAMESPACE;
1916 
1917         }
1918 
1919         return null;
1920 
1921     }
1922 
1923     public Namespace getNamespaceForURI(String uri) {
1924 
1925         if (uri == null || uri.length() <= 0) {
1926 
1927             return Namespace.NO_NAMESPACE;
1928 
1929         }
1930 
1931         else if (uri.equals(getNamespaceURI())) {
1932 
1933             return getNamespace();
1934 
1935         }
1936 
1937         else {
1938 
1939             List list = contentList();
1940 
1941             int size = list.size();
1942 
1943             for (int i = 0; i < size; i++) {
1944 
1945                 Object object = list.get(i);
1946 
1947                 if (object instanceof Namespace) {
1948 
1949                     Namespace namespace = (Namespace) object;
1950 
1951                     if (uri.equals(namespace.getURI())) {
1952 
1953                         return namespace;
1954 
1955                     }
1956 
1957                 }
1958 
1959             }
1960 
1961             return null;
1962 
1963         }
1964 
1965     }
1966 
1967     public List getNamespacesForURI(String uri) {
1968 
1969         BackedList answer = createResultList();
1970 
1971 //        if (getNamespaceURI().equals(uri)) {
1972 //
1973 //            answer.addLocal(getNamespace());
1974 //
1975 //        }
1976 
1977         List list = contentList();
1978 
1979         int size = list.size();
1980 
1981         for (int i = 0; i < size; i++) {
1982 
1983             Object object = list.get(i);
1984 
1985             if ((object instanceof Namespace) 
1986 
1987                 && ((Namespace) object).getURI().equals(uri)) {
1988                 
1989                     answer.addLocal(object);
1990                     
1991             }
1992 
1993         }
1994 
1995         return answer;
1996         
1997     }
1998 
1999     public List declaredNamespaces() {
2000 
2001         BackedList answer = createResultList();
2002 
2003 //        if (getNamespaceURI().length() > 0) {
2004 //
2005 //            answer.addLocal(getNamespace());
2006 //
2007 //        }
2008 //
2009         List list = contentList();
2010 
2011         int size = list.size();
2012 
2013         for (int i = 0; i < size; i++) {
2014 
2015             Object object = list.get(i);
2016 
2017             if (object instanceof Namespace) {
2018 
2019                 answer.addLocal(object);
2020 
2021             }
2022 
2023         }
2024 
2025         return answer;
2026 
2027     }
2028 
2029     public List additionalNamespaces() {
2030 
2031         List list = contentList();
2032 
2033         int size = list.size();
2034 
2035         BackedList answer = createResultList();
2036 
2037         for (int i = 0; i < size; i++) {
2038 
2039             Object object = list.get(i);
2040 
2041             if (object instanceof Namespace) {
2042 
2043                 Namespace namespace = (Namespace) object;
2044                 
2045                 if (! namespace.equals(getNamespace())) {
2046 
2047                     answer.addLocal(namespace);
2048                 }
2049 
2050             }
2051 
2052         }
2053 
2054         return answer;
2055 
2056     }
2057 
2058     public List additionalNamespaces(String defaultNamespaceURI) {
2059 
2060         List list = contentList();
2061 
2062         BackedList answer = createResultList();
2063 
2064         int size = list.size();
2065 
2066         for (int i = 0; i < size; i++) {
2067 
2068             Object object = list.get(i);
2069 
2070             if (object instanceof Namespace) {
2071 
2072                 Namespace namespace = (Namespace) object;
2073 
2074                 if (!defaultNamespaceURI.equals(namespace.getURI())) {
2075 
2076                     answer.addLocal(namespace);
2077 
2078                 }
2079 
2080             }
2081 
2082         }
2083 
2084         return answer;
2085 
2086     }
2087 
2088     // Implementation helper methods
2089 
2090     //-------------------------------------------------------------------------    
2091 
2092     /*** Ensures that the list of attributes has the given size */
2093 
2094     public void ensureAttributesCapacity(int minCapacity) {
2095 
2096         if (minCapacity > 1) {
2097 
2098             List list = attributeList();
2099 
2100             if (list instanceof ArrayList) {
2101 
2102                 ArrayList arrayList = (ArrayList) list;
2103 
2104                 arrayList.ensureCapacity(minCapacity);
2105 
2106             }
2107 
2108         }
2109 
2110     }
2111 
2112     // Implementation methods
2113 
2114     //-------------------------------------------------------------------------    
2115 
2116     protected Element createElement(String name) {
2117 
2118         return getDocumentFactory().createElement(name);
2119 
2120     }
2121 
2122     protected Element createElement(QName qName) {
2123 
2124         return getDocumentFactory().createElement(qName);
2125 
2126     }
2127 
2128     protected void addNode(Node node) {
2129 
2130         if (node.getParent() != null) {
2131 
2132             // XXX: could clone here
2133 
2134             String message =
2135                 "The Node already has an existing parent of \""
2136                     + node.getParent().getQualifiedName()
2137                     + "\"";
2138 
2139             throw new IllegalAddException(this, node, message);
2140 
2141         }
2142 
2143         addNewNode(node);
2144 
2145     }
2146     
2147     protected void addNode(int index, Node node) {
2148         
2149         if (node.getParent() != null) {
2150 
2151             // XXX: could clone here
2152 
2153             String message =
2154                 "The Node already has an existing parent of \""
2155                     + node.getParent().getQualifiedName()
2156                     + "\"";
2157 
2158             throw new IllegalAddException(this, node, message);
2159 
2160         }
2161 
2162         addNewNode(index, node);
2163 
2164     }
2165 
2166     /*** Like addNode() but does not require a parent check */
2167 
2168     protected void addNewNode(Node node) {
2169 
2170         contentList().add(node);
2171 
2172         childAdded(node);
2173 
2174     }
2175 
2176     protected void addNewNode(int index, Node node) {
2177 
2178         contentList().add(index, node);
2179 
2180         childAdded(node);
2181 
2182     }
2183 
2184     protected boolean removeNode(Node node) {
2185 
2186         boolean answer = contentList().remove(node);
2187 
2188         if (answer) {
2189 
2190             childRemoved(node);
2191 
2192         }
2193 
2194         return answer;
2195 
2196     }
2197 
2198     /*** Called when a new child node is added to
2199       * create any parent relationships
2200       */
2201 
2202     protected void childAdded(Node node) {
2203 
2204         if (node != null) {
2205 
2206             node.setParent(this);
2207 
2208         }
2209 
2210     }
2211 
2212     protected void childRemoved(Node node) {
2213 
2214         if (node != null) {
2215 
2216             node.setParent(null);
2217 
2218             node.setDocument(null);
2219 
2220         }
2221 
2222     }
2223 
2224     /*** @return the internal List used to store attributes or
2225       * creates one if one is not available
2226       */
2227 
2228     protected abstract List attributeList();
2229 
2230     /*** @return the internal List used to store attributes or
2231       * creates one with the specified size if one is not available
2232       */
2233 
2234     protected abstract List attributeList(int attributeCount);
2235 
2236     protected DocumentFactory getDocumentFactory() {
2237 
2238         QName qName = getQName();
2239 
2240         // QName might be null as we might not have been constructed yet
2241 
2242         if (qName != null) {
2243 
2244             DocumentFactory factory = qName.getDocumentFactory();
2245 
2246             if (factory != null) {
2247 
2248                 return factory;
2249 
2250             }
2251 
2252         }
2253 
2254         return DOCUMENT_FACTORY;
2255 
2256     }
2257 
2258     /*** A Factory Method pattern which creates 
2259       * a List implementation used to store attributes
2260       */
2261 
2262     protected List createAttributeList() {
2263 
2264         return createAttributeList(DEFAULT_CONTENT_LIST_SIZE);
2265 
2266     }
2267 
2268     /*** A Factory Method pattern which creates 
2269       * a List implementation used to store attributes
2270       */
2271 
2272     protected List createAttributeList(int size) {
2273 
2274         return new ArrayList(size);
2275 
2276     }
2277 
2278     protected Iterator createSingleIterator(Object result) {
2279 
2280         return new SingleIterator(result);
2281 
2282     }
2283 
2284 }
2285 
2286 /*
2287  * Redistribution and use of this software and associated documentation
2288  * ("Software"), with or without modification, are permitted provided
2289  * that the following conditions are met:
2290  *
2291  * 1. Redistributions of source code must retain copyright
2292  *    statements and notices.  Redistributions must also contain a
2293  *    copy of this document.
2294  *
2295  * 2. Redistributions in binary form must reproduce the
2296  *    above copyright notice, this list of conditions and the
2297  *    following disclaimer in the documentation and/or other
2298  *    materials provided with the distribution.
2299  *
2300  * 3. The name "DOM4J" must not be used to endorse or promote
2301  *    products derived from this Software without prior written
2302  *    permission of MetaStuff, Ltd.  For written permission,
2303  *    please contact dom4j-info@metastuff.com.
2304  *
2305  * 4. Products derived from this Software may not be called "DOM4J"
2306  *    nor may "DOM4J" appear in their names without prior written
2307  *    permission of MetaStuff, Ltd. DOM4J is a registered
2308  *    trademark of MetaStuff, Ltd.
2309  *
2310  * 5. Due credit should be given to the DOM4J Project - 
2311  *    http://www.dom4j.org
2312  *
2313  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
2314  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
2315  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2316  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
2317  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
2318  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2319  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2320  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2321  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2322  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2323  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2324  * OF THE POSSIBILITY OF SUCH DAMAGE.
2325  *
2326  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
2327  *
2328  * $Id: AbstractElement.java,v 1.77 2004/06/25 12:34:49 maartenc Exp $
2329  */