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