001 /*
002 // $Id: //open/util/resgen/src/org/eigenbase/xom/ElementDef.java#5 $
003 // Package org.eigenbase.xom is an XML Object Mapper.
004 // Copyright (C) 2005-2005 The Eigenbase Project
005 // Copyright (C) 2005-2005 Disruptive Tech
006 // Copyright (C) 2005-2005 LucidEra, Inc.
007 // Portions Copyright (C) 2000-2005 Kana Software, Inc. and others.
008 //
009 // This library is free software; you can redistribute it and/or modify it
010 // under the terms of the GNU Lesser General Public License as published by the
011 // Free Software Foundation; either version 2 of the License, or (at your
012 // option) any later version approved by The Eigenbase Project.
013 //
014 // This library is distributed in the hope that it will be useful,
015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017 // GNU Lesser General Public License for more details.
018 //
019 // You should have received a copy of the GNU Lesser General Public License
020 // along with this library; if not, write to the Free Software
021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022 //
023 // dsommerfield, 6 November, 2000
024 */
025
026 package org.eigenbase.xom;
027
028 import java.io.*;
029 import java.lang.reflect.Constructor;
030 import java.lang.reflect.Field;
031 import java.lang.reflect.InvocationTargetException;
032 import java.util.*;
033
034 /**
035 * ElementDef is the base class for all element definitions. It specifies the
036 * basic interface as well as provides useful services for all elements.
037 **/
038 public abstract class ElementDef implements NodeDef, Serializable, Cloneable
039 {
040
041 /**
042 * getElementClass is a static helper function which finds the XMLDef class
043 * corresponding to an Element. The Element's tag must start with the
044 * given prefix name, and
045 * the remainder of the tag must correspond to a static inner class of
046 * the given enclosing class.
047 * @param wrapper the DOMWrapper whose class to look up.
048 * @param enclosure a Class which encloses the Class to lookup.
049 * @param prefix a prefix which must appear on the tag name.
050 * @return the ElementDef Class corresponding to the element, or null if no
051 * class could be found (possible if this is a String element.
052 */
053 public static Class getElementClass(DOMWrapper wrapper,
054 Class enclosure,
055 String prefix)
056 throws XOMException
057 {
058 if (enclosure == null) {
059 // don't try to find a class -- they will use GenericDef
060 return null;
061 }
062 // wrapper must be of ELEMENT type. If not, throw a XOMException.
063 if (wrapper.getType() != DOMWrapper.ELEMENT) {
064 throw new XOMException("DOMWrapper must be of ELEMENT type.");
065 }
066 // Retrieve the tag name. It must start with the prefix.
067 String tag = wrapper.getTagName();
068 if (prefix == null) {
069 prefix = "";
070 } else if (!tag.startsWith(prefix)) {
071 throw new XOMException(
072 "Element names must start "
073 + "\"" + prefix + "\": "
074 + tag + " is invalid.");
075 }
076
077 // Remove the prefix and look for the name in the _elements field
078 // of the enclosure class. Note that the lookup is case-sensitive
079 // even though XML tags are not.
080 String className = tag.substring(prefix.length(), tag.length());
081 className = XOMUtil.capitalize(className);
082 Class elemClass = null;
083 try {
084 elemClass = Class.forName(enclosure.getName() + "$"
085 + className);
086 } catch (ClassNotFoundException ex) {
087 return null;
088 }
089 return elemClass;
090 }
091
092 /**
093 * constructElement is a helper function which builds the appropriate type
094 * of ElementDef from an XML Element. This version of the function takes
095 * an Element and a Class object specifying the exact class to use in
096 * constructing the element.
097 *
098 * @param wrapper the DOM Element wrapper from which to build this class.
099 * @param elemClass the Class to use to construct this class. It must have
100 * a constructor which takes the Element type.
101 * @return a fully constructed ElementDef of the type specified by
102 * Class.
103 * @throws XOMException if clazz has no constructor which takes Element,
104 * or if construction fails.
105 */
106 public static NodeDef constructElement(DOMWrapper wrapper,
107 Class elemClass)
108 throws XOMException
109 {
110 // Find a constructor of this class which takes an "Element" object
111 Constructor[] constructors = elemClass.getDeclaredConstructors();
112 Constructor elemConstructor = null;
113 for (int i = 0; i < constructors.length; i++) {
114 Class[] params = constructors[i].getParameterTypes();
115 if (params.length == 1 && params[0] == DOMWrapper.class) {
116 elemConstructor = constructors[i];
117 break;
118 }
119 }
120 if (elemConstructor == null) {
121 throw new XOMException(
122 "No constructor taking class DOMWrapper "
123 + "could be found in class "
124 + elemClass.getName());
125 }
126
127 // Call the constructor to instantiate the object
128 Object[] args = new Object[1];
129 args[0] = wrapper;
130 try {
131 return (ElementDef)(elemConstructor.newInstance(args));
132 } catch (InstantiationException ex) {
133 throw new XOMException("Unable to instantiate object of class "
134 + elemClass.getName() + ": "
135 + ex.getMessage());
136 } catch (InvocationTargetException ex) {
137 // the Element constructor can only throw XOMException or
138 // RuntimeException or Error, so cast to whichever type is appropriate
139 // and throw here.
140 Throwable target = ex.getTargetException();
141 if (target instanceof XOMException) {
142 throw (XOMException) target;
143 } else if (target instanceof RuntimeException) {
144 throw (RuntimeException) target;
145 } else if (target instanceof Error) {
146 throw (Error) target;
147 } else {
148 throw new XOMException(
149 "Unexpected exception while "
150 + "instantiating object: "
151 + target.toString());
152 }
153 } catch (IllegalAccessException ex) {
154 throw new XOMException("Unable to instantiate object of class "
155 + elemClass.getName() + ": "
156 + ex.getMessage());
157 }
158 }
159
160 /**
161 * constructElement is a helper function which builds the appropriate type
162 * of ElementDef from an XML Element. This function should be used when
163 * creating an ElementDef from a list of optional XML element types using
164 * ElementParser.requiredOption. Generally, it is better to call the
165 * constructors of ElementDef subclasses directly if the exact type of
166 * an element is known.
167 * @param wrapper the DOM Element Wrapper from which to build this class.
168 * @return an ElementDef whose exact type depends on the tag name of the
169 * element definition.
170 * @throws XOMException if no subclass of ElementDef can be found,
171 * or if def is malformed.
172 */
173 public static NodeDef constructElement(
174 DOMWrapper wrapper, Class enclosure, String prefix)
175 throws XOMException
176 {
177 switch (wrapper.getType()) {
178 case DOMWrapper.ELEMENT:
179 Class elemClass = getElementClass(wrapper, enclosure, prefix);
180 if (elemClass == null) {
181 if (true) {
182 return new WrapperElementDef(wrapper, enclosure, prefix);
183 } else {
184 throw new XOMException("No class corresponding to element "
185 + wrapper.getTagName()
186 + " could be found in enclosure "
187 + enclosure.getName());
188 }
189 } else {
190 return constructElement(wrapper, elemClass);
191 }
192 case DOMWrapper.COMMENT:
193 return new CommentDef(wrapper.getText());
194 case DOMWrapper.CDATA:
195 return new CdataDef(wrapper.getText());
196 case DOMWrapper.FREETEXT:
197 return new TextDef(wrapper.getText());
198 default:
199 throw new XOMException("Unknown type: " + wrapper.getText());
200 }
201 }
202
203
204
205 // implement NodeDef
206 public void displayXML(XMLOutput out, int indent) {}
207
208 public void displayXML(XMLOutput out)
209 {
210 displayXML(out, 0);
211 }
212
213 /**
214 * The displayDiff function compares this element definition against another,
215 * compiling a message containing all diffs. It is used internally by
216 * the equals(), diff(), and verifyEquals() functions.
217 * @param other the ElementDef to which to compare this element.
218 * @param out a PrintWriter to which to display any discovered differences,
219 * or null if just doing an equality check (and no diff report is needed).
220 * @param indent the current indentation level (used for nice display of diffs).
221 * @return true if this and other match exactly, false if not.
222 */
223 public boolean displayDiff(ElementDef other, PrintWriter out, int indent)
224 {
225 return false;
226 }
227
228 // implement NodeDef
229 public String getName()
230 {
231 return getClass().getName();
232 }
233
234 // implement NodeDef
235 public int getType()
236 {
237 return DOMWrapper.ELEMENT;
238 }
239
240 // implement NodeDef
241 public String getText()
242 {
243 return null;
244 }
245
246 /**
247 * This function writes an indentation level to the given PrintWriter.
248 * @param out the PrintWriter to which to write the indent.
249 * @param indent the indentation level
250 */
251 protected static void displayIndent(PrintWriter out, int indent)
252 {
253 for (int i = 0; i < indent; i++) {
254 out.print(" ");
255 }
256 }
257
258 /**
259 * This convenience function displays a String value with the given
260 * parameter name at the given indentation level. It is meant to be
261 * called by subclasses of ElementDef.
262 * @param out the PrintWriter to which to write this String.
263 * @param name the parameter name of this string.
264 * @param value the value of the String parameter.
265 * @param indent the indentation level.
266 */
267 protected static void displayString(
268 PrintWriter out,
269 String name,
270 String value,
271 int indent)
272 {
273 displayIndent(out, indent);
274 if (value == null) {
275 out.println(name + ": null");
276 } else {
277 out.println(name + ": \"" + value + "\"");
278 }
279 }
280
281 /**
282 * This convenience function displays an XML attribute value
283 * with the given attribute name at the given indentation level.
284 * It should be called by subclasses of ElementDef.
285 * @param out the PrintWriter to which to write this String.
286 * @param name the attribute name.
287 * @param value the attribute value.
288 * @param indent the indentation level.
289 */
290 protected static void displayAttribute(
291 PrintWriter out,
292 String name,
293 Object value,
294 int indent)
295 {
296 displayIndent(out, indent);
297 if (value == null) {
298 out.println(name + " = null");
299 } else {
300 out.println(name + " = \"" + value.toString() + "\"");
301 }
302 }
303
304 /**
305 * This convenience function displays any ElementDef with the given
306 * parameter name at the given indentation level.
307 * @param out the PrintWriter to which to write this ElementDef.
308 * @param name the parameter name for this ElementDef.
309 * @param value the parameter's value (as an ElementDef).
310 * @param indent the indentation level.
311 */
312 protected static void displayElement(
313 PrintWriter out,
314 String name,
315 ElementDef value,
316 int indent)
317 {
318 displayIndent(out, indent);
319 if (value == null) {
320 out.println(name + ": null");
321 } else {
322 out.print(name + ": ");
323 value.display(out, indent);
324 }
325 }
326
327 /**
328 * This convenience function displays any array of ElementDef values with
329 * the given parameter name (assumed to represent an array) at the given
330 * indentation level. Each value of the array will be written on a
331 * separate line with a new indentation.
332 * @param out the PrintWriter to which to write this ElementDef.
333 * @param name the parameter name for this ElementDef.
334 * @param values the parameter's values (as an ElementDef[] array).
335 * @param indent the indentation level.
336 */
337 protected static void displayElementArray(
338 PrintWriter out,
339 String name,
340 NodeDef[] values,
341 int indent)
342 {
343 displayIndent(out, indent);
344 if (values == null) {
345 out.println(name + ": null array");
346 } else {
347 out.println(name + ": array of " + values.length + " values");
348 for (int i = 0; i < values.length; i++) {
349 displayIndent(out, indent);
350 if (values[i] == null) {
351 out.println(name + "[" + i + "]: null");
352 } else {
353 out.print(name + "[" + i + "]: ");
354 values[i].display(out, indent);
355 }
356 }
357 }
358 }
359
360 /**
361 * This convenience function displays any array of String values with
362 * the given parameter name (assumed to represent an array) at the given
363 * indentation level. Each value of the array will be written on a
364 * separate line with a new indentation.
365 * @param out the PrintWriter to which to write this ElementDef.
366 * @param name the parameter name for this ElementDef.
367 * @param values the parameter's values (as a String[] array).
368 * @param indent the indentation level.
369 */
370 protected static void displayStringArray(
371 PrintWriter out,
372 String name,
373 String[] values,
374 int indent)
375 {
376 displayIndent(out, indent);
377 if (values == null) {
378 out.println(name + ": null array");
379 } else {
380 out.println(name + ": array of " + values.length + " values");
381 for (int i = 0; i < values.length; i++) {
382 displayIndent(out, indent);
383 if (values[i] == null) {
384 out.println(name + "[" + i + "]: null");
385 } else {
386 out.println(name + "[" + i + "]: " + values[i]);
387 }
388 }
389 }
390 }
391
392 /**
393 * This convenience function displays a String value in XML.
394 * parameter name at the given indentation level. It is meant to be
395 * called by subclasses of ElementDef.
396 * @param out XMLOutput class to which to generate XML.
397 * @param tag the Tag name of this String object.
398 * @param value the String value.
399 */
400 protected static void displayXMLString(
401 XMLOutput out,
402 String tag,
403 String value)
404 {
405 if (value != null) {
406 out.stringTag(tag, value);
407 }
408 }
409
410 /**
411 * This convenience function displays any ElementDef in XML.
412 * @param out the XMLOutput class to which to generate XML.
413 * @param value the ElementDef to display.
414 */
415 protected static void displayXMLElement(
416 XMLOutput out,
417 ElementDef value)
418 {
419 if (value != null) {
420 value.displayXML(out, 0);
421 }
422 }
423
424 /**
425 * This convenience function displays an array of ElementDef values in XML.
426 * @param out the XMLOutput class to which to generate XML.
427 * @param values the ElementDef to display.
428 */
429 protected static void displayXMLElementArray(
430 XMLOutput out,
431 NodeDef[] values)
432 {
433 if (values != null) {
434 for (int i = 0; i < values.length; i++) {
435 values[i].displayXML(out, 0);
436 }
437 }
438 }
439
440 /**
441 * This convenience function displays a String array in XML.
442 * @param out the XMLOutput class to which to generate XML.
443 * @param tag the tag name for the String elements.
444 * @param values the actual string values.
445 */
446 protected static void displayXMLStringArray(
447 XMLOutput out,
448 String tag,
449 String[] values)
450 {
451 for (int i = 0; i < values.length; i++) {
452 out.stringTag(tag, values[i]);
453 }
454 }
455
456 /**
457 * This convenience function displays differences in two versions of
458 * the same string object.
459 * @param name the object name.
460 * @param value1 the first string.
461 * @param value2 the second string.
462 * @param out the PrintWriter to which to write differences.
463 * @param indent the indentation level.
464 * @return true if the strings match, false if not.
465 */
466 protected static boolean displayStringDiff(
467 String name,
468 String value1,
469 String value2,
470 PrintWriter out,
471 int indent)
472 {
473 // True if both values are null.
474 if (value1 == null && value2 == null) {
475 return true;
476 }
477 // Deal with the cases where one value is set but the other is not.
478 if (value2 == null) {
479 if (out != null) {
480 displayIndent(out, indent);
481 out.println("String " + name + ": mismatch: "
482 + value1.toString() + " vs null.");
483 }
484 return false;
485 }
486 if (value1 == null) {
487 if (out != null) {
488 displayIndent(out, indent);
489 out.println("String " + name + ": mismatch: "
490 + "null vs " + value2.toString() + ".");
491 }
492 return false;
493 }
494
495 // Finally, check the values themselves
496 if (value1.equals(value2)) {
497 return true;
498 }
499 if (out != null) {
500 displayIndent(out, indent);
501 out.println("String " + name + ": mismatch: "
502 + value1.toString() + " vs "
503 + value2.toString() + ".");
504 }
505 return false;
506 }
507
508 /**
509 * This convenience function displays differences in two versions of
510 * the same XML attribute value.
511 * @param name the attribute name.
512 * @param value1 the first attribute value.
513 * @param value2 the second attribute value.
514 * @param out the PrintWriter to which to write differences.
515 * @param indent the indentation level.
516 * @return true if the values match, false if not.
517 */
518 protected static boolean displayAttributeDiff(
519 String name,
520 Object value1,
521 Object value2,
522 PrintWriter out,
523 int indent)
524 {
525 // True if both values are null.
526 if (value1 == null && value2 == null) {
527 return true;
528 }
529 // Deal with the cases where one value is set but the other is not.
530 if (value2 == null) {
531 if (out != null) {
532 displayIndent(out, indent);
533 out.println("Attribute " + name + ": mismatch: "
534 + value1.toString() + " vs null.");
535 }
536 return false;
537 }
538 if (value1 == null) {
539 if (out != null) {
540 displayIndent(out, indent);
541 out.println("Attribute " + name + ": mismatch: "
542 + "null vs " + value2.toString() + ".");
543 }
544 return false;
545 }
546
547 // Verify that types match
548 if (value1.getClass() != value2.getClass()) {
549 if (out != null) {
550 displayIndent(out, indent);
551 out.println("Attribute " + name + ": class mismatch: "
552 + value1.getClass().getName()
553 + " vs "
554 + value2.getClass().getName()
555 + ".");
556 }
557 return false;
558 }
559
560 // Finally, check the values themselves
561 if (value1.equals(value2)) {
562 return true;
563 }
564 if (out != null) {
565 displayIndent(out, indent);
566 out.println("Attribute " + name + ": mismatch: "
567 + value1.toString() + " vs "
568 + value2.toString() + ".");
569 }
570 return false;
571 }
572
573 /**
574 * This convenience function displays differences in the values of any
575 * two ElementDefs, returning true if they match and false if not.
576 * @param name the object name.
577 * @param value1 the first value.
578 * @param value2 the second value.
579 * @param out the PrintWriter to which to write differences.
580 * @param indent the indentation level.
581 * @return true if the values match, false if not.
582 */
583 protected static boolean displayElementDiff(
584 String name,
585 NodeDef value1,
586 NodeDef value2,
587 PrintWriter out,
588 int indent)
589 {
590 // True if both values are null.
591 if (value1 == null && value2 == null) {
592 return true;
593 }
594 // Deal with the cases where one value is set but the other is not.
595 if (value2 == null) {
596 if (out != null) {
597 displayIndent(out, indent);
598 out.println("Object " + name + ": mismatch: "
599 + "(...) vs null.");
600 }
601 return false;
602 }
603 if (value1 == null) {
604 if (out != null) {
605 displayIndent(out, indent);
606 out.println("Object " + name + ": mismatch: "
607 + "null vs (...).");
608 }
609 return false;
610 }
611
612 // Verify that types match
613 if (value1.getClass() != value2.getClass()) {
614 if (out != null) {
615 displayIndent(out, indent);
616 out.println("Object " + name + ": class mismatch: "
617 + value1.getClass().getName()
618 + " vs "
619 + value2.getClass().getName()
620 + ".");
621 }
622 return false;
623 }
624
625 // Do a sub equality check
626 return ((ElementDef) value1).displayDiff(
627 (ElementDef) value2, out, indent);
628 }
629
630 /**
631 * This convenience function diffs any array of ElementDef values with
632 * the given array name. All differences are written to the given
633 * PrintWriter at the given indentation level.
634 * @param name the array name.
635 * @param values1 the first array.
636 * @param values2 the second array.
637 * @param out the PrintWriter to which to write differences.
638 * @param indent the indentation level.
639 * @return true if the both arrays match, false if there are any differences.
640 */
641 protected static boolean displayElementArrayDiff(
642 String name,
643 NodeDef[] values1,
644 NodeDef[] values2,
645 PrintWriter out, int indent)
646 {
647 int length1 = 0;
648 int length2 = 0;
649 if (values1 != null) {
650 length1 = values1.length;
651 }
652 if (values2 != null) {
653 length2 = values2.length;
654 }
655 // Check array sizes
656 // a null array does not differ from an empty array
657 if (length1 != length2) {
658 if (out != null) {
659 displayIndent(out, indent);
660 out.println("Array " + name + ": size mismatch: "
661 + length1 + " vs "
662 + length2 + ".");
663 }
664 return false;
665 }
666
667 // Check each member of the array
668 boolean diff = true;
669 for (int i = 0; i < length1; i++) {
670 diff = diff
671 && displayElementDiff(
672 name + "[" + i + "]",
673 values1[i], values2[i],
674 out, indent);
675 }
676 return diff;
677 }
678
679 /**
680 * This convenience function diffs any array of strings with
681 * the given array name. All differences are written to the given
682 * PrintWriter at the given indentation level.
683 * @param name the array name.
684 * @param values1 the first array.
685 * @param values2 the second array.
686 * @param out the PrintWriter to which to write differences.
687 * @param indent the indentation level.
688 * @return true if the both arrays match, false if there are any differences.
689 */
690 protected static boolean displayStringArrayDiff(
691 String name,
692 String[] values1,
693 String[] values2,
694 PrintWriter out,
695 int indent)
696 {
697 // Check array sizes
698 if (values1.length != values2.length) {
699 if (out != null) {
700 displayIndent(out, indent);
701 out.println("Array " + name + ": size mismatch: "
702 + values1.length + " vs "
703 + values2.length + ".");
704 }
705 return false;
706 }
707
708 // Check each member of the array
709 boolean diff = true;
710 for (int i = 0; i < values1.length; i++) {
711 diff = diff
712 && displayStringDiff(
713 name + "[" + i + "]",
714 values1[i], values2[i],
715 out, indent);
716 }
717 return diff;
718 }
719
720 /**
721 * The toString function automatically uses display() to produce a string
722 * version of this ElementDef. The indentation level is always zero.
723 */
724 public String toString()
725 {
726 StringWriter strOut = new StringWriter();
727 PrintWriter prOut = new PrintWriter(strOut);
728 display(prOut, 0);
729 return strOut.toString();
730 }
731
732 /**
733 * The toXML function automatically uses displayXML() to produce an XML
734 * version of this ElementDef as a String.
735 * @return an XML representation of this ElementDef, as a String.
736 */
737 public String toXML()
738 {
739 StringWriter writer = new StringWriter();
740 XMLOutput out = new XMLOutput(writer);
741 displayXML(out, 0);
742 return writer.toString();
743 }
744
745 /**
746 * The toCompactXML function automatically uses displayXML() to produce an XML
747 * version of this ElementDef as a String. The generated XML is
748 * <i>compact</i>; free of unnecessary whitespace. Compact XML is useful
749 * when embedding XML in a CDATA section or transporting over the network.
750 * @return an XML representation of this ElementDef, as a String.
751 */
752 public String toCompactXML()
753 {
754 StringWriter writer = new StringWriter();
755 XMLOutput out = new XMLOutput(writer);
756 out.setCompact(true);
757 displayXML(out, 0);
758 return writer.toString();
759 }
760
761 /**
762 * The diff function compares this element against another, determining if
763 * they are exactly equal. If so, the function returns null. If not,
764 * it returns a String describing the differences.
765 */
766 public String diff(ElementDef other)
767 {
768 StringWriter writer = new StringWriter();
769 PrintWriter out = new PrintWriter(writer);
770 boolean diff = displayDiff(other, out, 0);
771 if (!diff) {
772 return writer.toString();
773 } else {
774 return null;
775 }
776 }
777
778 /**
779 * Determines if this ElementDef is equal to other (deeply), returning true
780 * if the two are equal.
781 * @return true if this equals other, false if not.
782 * @throws ClassCastException if other is not an ElementDef.
783 */
784 public boolean equals(Object other)
785 {
786 try {
787 return displayDiff((ElementDef)other, null, 0);
788 } catch (ClassCastException ex) {
789 return false;
790 }
791 }
792
793 /**
794 * Returns a unique hash of this instance.
795 * @return hash of the toXML() return value
796 */
797 public int hashCode()
798 {
799 return this.toXML().hashCode();
800 }
801
802 /**
803 * Verifies that this ElementDef is equal to other, throwing a
804 * XOMException with a lengthy explanation if equality
805 * fails.
806 * @param other the ElementDef to compare to this one.
807 */
808 public void verifyEqual(ElementDef other)
809 throws XOMException
810 {
811 StringWriter writer = new StringWriter();
812 PrintWriter out = new PrintWriter(writer);
813 out.println();
814 boolean diff = displayDiff(other, out, 1);
815 out.println();
816 if (!diff) {
817 throw new XOMException(
818 "Element definition mismatch: "
819 + writer.toString());
820 }
821 }
822
823 /**
824 * Clone an ElementDef. Because all ElementDefs are serializable, we can
825 * clone through a memory buffer.
826 */
827 protected Object clone()
828 throws CloneNotSupportedException
829 {
830 try {
831 return deepCopy();
832 } catch (XOMException ex) {
833 throw new CloneNotSupportedException(
834 "Unable to clone " + getClass().getName() + ": "
835 + ex.toString());
836 }
837 }
838
839 /**
840 * Public version of clone(); returns a deep copy of this ElementDef.
841 */
842 public ElementDef deepCopy()
843 throws XOMException
844 {
845 try {
846 ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
847 ObjectOutputStream objOut = new ObjectOutputStream(byteBuffer);
848 objOut.writeObject(this);
849 objOut.flush();
850 ByteArrayInputStream byteIn = new
851 ByteArrayInputStream(byteBuffer.toByteArray());
852 ObjectInputStream objIn = new ObjectInputStream(byteIn);
853 ElementDef def = (ElementDef)(objIn.readObject());
854 return def;
855 } catch (IOException ex) {
856 throw new XOMException(ex, "Failed to serialize-copy ElementDef");
857 } catch (ClassNotFoundException ex) {
858 throw new XOMException(ex, "Failed to serialize-copy ElementDef");
859 }
860 }
861
862 // implement NodeDef
863 public DOMWrapper getWrapper()
864 {
865 try {
866 Field field = getClass().getField("_def");
867 return (DOMWrapper) field.get(this);
868 } catch (NoSuchFieldException ex) {
869 return null;
870 } catch (IllegalAccessException ex) {
871 throw new Error(ex.toString() + " in getWrapper");
872 }
873 }
874
875 // implement NodeDef
876 public NodeDef[] getChildren()
877 {
878 List childrenList = new ArrayList();
879 final Field[] fields = getClass().getFields();
880 for (int i = 0; i < fields.length; i++) {
881 Field field = fields[i];
882 try {
883 final Class type = field.getType();
884 if (NodeDef.class.isAssignableFrom(type)) {
885 childrenList.add((NodeDef) field.get(this));
886 } else if (type.isArray()
887 && NodeDef.class.isAssignableFrom(
888 type.getComponentType())) {
889 NodeDef[] nodes = (NodeDef[]) field.get(this);
890 childrenList.addAll(Arrays.asList(nodes));
891 }
892 } catch (IllegalAccessException e) {
893 throw new RuntimeException(
894 "Error while accessing field '" + field + "'", e);
895 }
896 }
897 return
898 (NodeDef[]) childrenList.toArray(new NodeDef[childrenList.size()]);
899 }
900
901 public void addChild(NodeDef child) throws XOMException
902 {
903 XOMUtil.addChild(this, child);
904 }
905
906 public void addChildren(NodeDef[] children) throws XOMException
907 {
908 XOMUtil.addChildren(this, children);
909 }
910
911 protected static NodeDef[] getMixedChildren_new(
912 DOMWrapper _def, Class clazz, String prefix) throws XOMException
913 {
914 DOMWrapper[] _elts = _def.getChildren();
915 int count = 0;
916 for (int i = 0; i < _elts.length; i++) {
917 switch (_elts[i].getType()) {
918 case DOMWrapper.ELEMENT:
919 case DOMWrapper.CDATA:
920 case DOMWrapper.COMMENT:
921 count++;
922 break;
923 case DOMWrapper.FREETEXT:
924 default:
925 break;
926 }
927 }
928 NodeDef[] children = new NodeDef[count];
929 count = 0;
930 for (int i = 0; i < _elts.length; i++) {
931 switch (_elts[i].getType()) {
932 case DOMWrapper.ELEMENT:
933 case DOMWrapper.CDATA:
934 case DOMWrapper.COMMENT:
935 children[count++] = constructElement(_elts[i], clazz, prefix);
936 break;
937 case DOMWrapper.FREETEXT:
938 default:
939 break;
940 }
941 }
942 return children;
943 }
944
945 protected static NodeDef[] getMixedChildren(
946 DOMWrapper _def, Class clazz, String prefix) throws XOMException
947 {
948 DOMWrapper[] _elts = _def.getChildren();
949 NodeDef[] children = new NodeDef[_elts.length];
950 for (int i = 0; i < _elts.length; i++) {
951 children[i] = constructElement(_elts[i], clazz, prefix);
952 }
953 return children;
954 }
955
956 protected static ElementDef[] getElementChildren(
957 DOMWrapper _def, Class clazz, String prefix) throws XOMException
958 {
959 DOMWrapper[] _elts = _def.getElementChildren();
960 ElementDef[] children = new ElementDef[_elts.length];
961 for (int i = 0; i < children.length; i++) {
962 children[i] = (ElementDef) constructElement(
963 _elts[i], clazz, prefix);
964 }
965 return children;
966 }
967
968 public Location getLocation() {
969 final DOMWrapper wrapper = getWrapper();
970 if (wrapper == null) {
971 return null;
972 } else {
973 return wrapper.getLocation();
974 }
975 }
976 }
977
978 // End ElementDef.java