1 7 package org.jboss.xml.binding; 8 9 import org.jboss.logging.Logger; 10 import org.jboss.util.Classes; 11 import org.jboss.util.NestedRuntimeException; 12 import org.xml.sax.Attributes ; 13 import org.apache.xerces.xs.XSTypeDefinition; 14 15 import java.lang.reflect.Method ; 16 import java.lang.reflect.Constructor ; 17 import java.util.Map ; 18 import java.util.HashMap ; 19 import java.util.Collection ; 20 import java.util.ArrayList ; 21 import java.util.List ; 22 23 30 public class MappingObjectModelFactory 31 implements GenericObjectModelFactory 32 { 33 private final static Logger log = Logger.getLogger(MappingObjectModelFactory.class); 34 35 38 private final Map elementToClassMapping = new HashMap (); 39 40 43 private final Map elementToFieldMapping = new HashMap (); 44 45 47 53 public void mapElementToClass(String element, Class cls) 54 { 55 ElementToClassMapping mapping = new ElementToClassMapping(element, cls); 56 addElementToClassMapping(mapping); 57 if(log.isTraceEnabled()) 58 { 59 log.trace(mapping); 60 } 61 } 62 63 71 public void mapElementToField(String element, Class cls, String field, TypeConverter converter) 72 { 73 ElementToFieldMapping mapping = new ElementToFieldMapping(element, cls, field, converter); 74 addElementToFieldMapping(mapping); 75 if(log.isTraceEnabled()) 76 { 77 log.trace(mapping); 78 } 79 } 80 81 83 public Object newRoot(Object root, 84 ContentNavigator navigator, 85 String namespaceURI, 86 String localName, 87 Attributes attrs) 88 { 89 if(log.isTraceEnabled()) 90 { 91 log.trace("newRoot root=" + 92 root + 93 " navigator=" + 94 navigator + 95 " namespaceURI=" + 96 namespaceURI + 97 " localName=" + 98 localName + 99 " attributes=" + 100 attrs 101 ); 102 } 103 104 if(root == null) 105 { 106 ElementToClassMapping mapping = (ElementToClassMapping)elementToClassMapping.get(localName); 107 if(mapping != null) 108 { 109 if(log.isTraceEnabled()) 110 { 111 log.trace("creating root using " + mapping); 112 } 113 root = newInstance(mapping.cls); 114 } 115 else 116 { 117 root = create(namespaceURI, localName, navigator.getType()); 118 } 119 120 if(root == null) 121 { 122 throw new IllegalStateException ( 123 "Failed to resolve Java type binding for root element: ns=" + namespaceURI + ", local=" + localName 124 ); 125 } 126 } 127 128 if(attrs != null) 129 { 130 for(int i = 0; i < attrs.getLength(); ++i) 131 { 132 try 133 { 134 if(attrs.getLocalName(i).length() > 0) 135 { 136 if(!attrs.getQName(i).startsWith("xsi:")) { 138 setAttribute(root, attrs.getLocalName(i), attrs.getValue(i), navigator.getType()); 139 } 140 } 141 } 142 catch(Exception e) 143 { 144 String msg = "Failed to set attribute " + attrs.getQName(i) + "=" + attrs.getValue(i); 145 log.error(msg, e); 146 throw new IllegalStateException (msg + ": " + e.getMessage()); 147 } 148 } 149 } 150 151 return root; 152 } 153 154 156 public Object newChild(Object o, 157 ContentNavigator navigator, 158 String namespaceURI, 159 String localName, 160 Attributes attrs) 161 { 162 if(log.isTraceEnabled()) 163 { 164 log.trace("newChild object=" + 165 o + 166 " navigator=" + 167 navigator + 168 " namespaceURI=" + 169 namespaceURI + 170 " localName=" + 171 localName + 172 " attributes=" + 173 attrs 174 ); 175 } 176 177 if(o == null) 178 { 179 throw new RuntimeException ("Attempt to add a new child to a null parent localName=" + localName); 180 } 181 182 Object child = null; 183 184 ElementToClassMapping mapping = (ElementToClassMapping)elementToClassMapping.get(localName); 185 XSTypeDefinition type = navigator.getType(); 186 if(mapping != null) 187 { 188 if(log.isTraceEnabled()) 189 { 190 log.trace("newChild using mapping " + mapping); 191 } 192 193 try 194 { 195 if(!(o instanceof Collection )) 196 { 197 Method getter; 198 ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get( 199 new ElementToFieldMappingKey(localName, o.getClass()) 200 ); 201 202 if(fieldMapping != null) 203 { 204 getter = fieldMapping.getter; 205 } 206 else 207 { 208 String getterStr = Util.xmlNameToGetMethodName(localName, true); 209 getter = o.getClass().getMethod(getterStr, null); 210 } 211 child = get(o, localName, getter); 212 } 213 214 if(child == null) 215 { 216 child = newInstance(mapping.cls); 217 } 218 219 if(attrs != null) 220 { 221 for(int i = 0; i < attrs.getLength(); ++i) 222 { 223 if(attrs.getLocalName(i).length() > 0) 224 { 225 if(!attrs.getQName(i).startsWith("xsi:")) { 227 setAttribute(child, attrs.getLocalName(i), attrs.getValue(i), type); 228 } 229 } 230 } 231 } 232 } 233 catch(IllegalStateException e) 234 { 235 throw e; 236 } 237 catch(Exception e) 238 { 239 throw new NestedRuntimeException("newChild failed for o=" + 240 o + 241 ", uri=" + 242 namespaceURI + 243 ", local=" 244 + localName + ", attrs=" + attrs, e 245 ); 246 } 247 } 248 else 249 { 250 if(o instanceof Collection ) 251 { 252 child = create(namespaceURI, localName, type); 253 } 254 else 255 { 256 Class oCls; 257 if(o instanceof ImmutableContainer) 258 { 259 oCls = ((ImmutableContainer)o).cls; 260 } 261 else 262 { 263 oCls = o.getClass(); 264 } 265 266 String getterStr = Util.xmlNameToGetMethodName(localName, true); 267 Method getter; 268 try 269 { 270 getter = oCls.getMethod(getterStr, null); 271 } 272 catch(NoSuchMethodException e) 273 { 274 throw new IllegalStateException ("newChild failed for o=" + 275 o + 276 ", uri=" + 277 namespaceURI + 278 ", local=" 279 + localName + ", attrs=" + attrs + ": no getter" 280 ); 281 } 282 283 Class childType = getter.getReturnType(); 284 if(Collection .class.isAssignableFrom(childType)) 285 { 286 child = get(o, localName, getter); 287 288 Object item = null; 291 if(type == null || type != null && type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) 292 { 293 item = create(namespaceURI, localName, type); 294 } 295 296 if(item != null) 297 { 298 if(child == null) 299 { 300 setChild(new ArrayList (), o, localName); 301 } 302 child = item; 303 } 304 else 305 { 306 if(child == null) 307 { 308 child = new ArrayList (); 309 } 310 } 311 } 312 else if(!Util.isAttributeType(childType)) 313 { 314 ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get( 316 new ElementToFieldMappingKey(localName, o.getClass()) 317 ); 318 TypeConverter converter = fieldMapping == null ? null : fieldMapping.converter; 319 320 if(converter == null) 322 { 323 child = newInstance(childType); 324 } 325 } 326 } 327 } 328 329 return child; 330 } 331 332 public void addChild(Object parent, 333 Object child, 334 ContentNavigator navigator, 335 String namespaceURI, 336 String localName) 337 { 338 if(log.isTraceEnabled()) 339 { 340 log.trace("addChild parent=" + 341 parent + 342 " child=" + 343 child + 344 " navigator=" + 345 navigator + 346 " namespaceURI=" + 347 namespaceURI + 348 " localName=" + 349 localName 350 ); 351 } 352 353 if(child instanceof ImmutableContainer) 354 { 355 child = ((ImmutableContainer)child).newInstance(); 356 } 357 setChild(child, parent, localName); 358 } 359 360 public void setValue(Object o, ContentNavigator navigator, String namespaceURI, String localName, String value) 361 { 362 if(log.isTraceEnabled()) 363 { 364 log.trace("setValue object=" + 365 o + 366 " navigator=" + 367 navigator + 368 " namespaceURI=" + 369 namespaceURI + 370 " localName=" + 371 localName + 372 " value=" + 373 value 374 ); 375 } 376 377 setAttribute(o, localName, value, navigator.getType()); 378 } 379 380 public Object completedRoot(Object root, ContentNavigator navigator, String namespaceURI, String localName) 381 { 382 if(log.isTraceEnabled()) 383 { 384 log.trace("completedRoot root=" + 385 root + 386 " navigator=" + 387 navigator + 388 " namespaceURI=" + 389 namespaceURI + 390 " localName=" + 391 localName 392 ); 393 } 394 395 if(root instanceof ImmutableContainer) 396 { 397 root = ((ImmutableContainer)root).newInstance(); 398 } 399 return root; 400 } 401 402 404 private void addElementToClassMapping(ElementToClassMapping mapping) 405 { 406 elementToClassMapping.put(mapping.element, mapping); 407 } 408 409 private void addElementToFieldMapping(ElementToFieldMapping mapping) 410 { 411 elementToFieldMapping.put(mapping.key, mapping); 412 } 413 414 private void setChild(Object child, Object parent, String localName) 415 { 416 boolean trace = log.isTraceEnabled(); 417 Object value = child; 418 if(parent instanceof Collection ) 419 { 420 if(trace) 421 { 422 log.trace("Add " + value + " to collection " + parent); 423 } 424 ((Collection )parent).add(value); 425 } 426 else 427 { 428 Method setter = null; 429 final ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get( 430 new ElementToFieldMappingKey(localName, parent.getClass()) 431 ); 432 if(fieldMapping != null) 433 { 434 if(trace) 435 { 436 log.trace("Add " + value + " to " + parent + " using field mapping " + fieldMapping); 437 } 438 setter = fieldMapping.setter; 439 set(parent, value, localName, setter); 440 } 441 else 442 { 443 final String xmlToCls = Util.xmlNameToClassName(localName, true); 444 if(trace) 445 { 446 log.trace("Add " + value + " to xml mapped class " + xmlToCls); 447 } 448 Method getter = null; 449 Class parentCls; 450 if(parent instanceof ImmutableContainer) 451 { 452 parentCls = ((ImmutableContainer)parent).cls; 453 } 454 else 455 { 456 parentCls = parent.getClass(); 457 } 458 459 try 460 { 461 getter = parentCls.getMethod("get" + xmlToCls, null); 462 } 463 catch(NoSuchMethodException e) 464 { 465 log.warn("no getter found for " + localName + " in " + parent); 466 } 467 468 if(getter != null) 469 { 470 if(!(child instanceof Collection ) && Collection .class.isAssignableFrom(getter.getReturnType())) 471 { 472 Object o = get(parent, localName, getter); 473 Collection col = (Collection )o; 474 if(trace) 475 { 476 log.trace("Add " + value + " to collection " + col + " retrieved from getter " + getter); 477 } 478 col.add(child); 479 } 480 else 481 { 482 483 try 484 { 485 setter = parentCls.getMethod("set" + xmlToCls, new Class []{getter.getReturnType()}); 486 } 487 catch(NoSuchMethodException e) 488 { 489 log.warn("No setter for " + localName + " in " + parentCls); 490 } 491 492 set(parent, value, localName, setter); 493 } 494 } 495 } 496 } 497 } 498 499 private void setAttribute(Object o, String localName, String value, XSTypeDefinition type) 500 { 501 if(o instanceof Collection ) 502 { 503 if(type == null) 504 { 505 log.warn("Type is not available for collection item " + localName + "=" + value + " -> adding as string."); 506 ((Collection )o).add(value); 507 } 508 else 509 { 510 if(type.getName() == null) 511 { 512 throw new IllegalStateException ("Name is null for simple type?!"); 513 } 514 515 Object trgValue = TypeBinding.unmarshal(type.getName(), value); 516 ((Collection )o).add(trgValue); 517 } 518 } 519 else 520 { 521 Method setter = null; 522 Object fieldValue = null; 523 final ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get( 524 new ElementToFieldMappingKey(localName, o.getClass()) 525 ); 526 if(fieldMapping != null) 527 { 528 fieldValue = fieldMapping.converter.unmarshal(value); 529 setter = fieldMapping.setter; 530 } 531 else 532 { 533 Class oCls; 534 if(o instanceof ImmutableContainer) 535 { 536 oCls = ((ImmutableContainer)o).cls; 537 } 538 else 539 { 540 oCls = o.getClass(); 541 } 542 543 try 544 { 545 final String xmlToCls = Util.xmlNameToClassName(localName, true); 546 Method getter = oCls.getMethod("get" + xmlToCls, null); 547 fieldValue = TypeBinding.unmarshal(value, getter.getReturnType()); 548 setter = oCls.getMethod("set" + xmlToCls, new Class []{getter.getReturnType()}); 549 } 550 catch(NoSuchMethodException e) 551 { 552 log.warn("no setter found for " + localName + " in " + oCls); 553 } 554 } 555 556 set(o, fieldValue, localName, setter); 557 } 558 } 559 560 568 private static Object create(String namespaceURI, String localName, XSTypeDefinition type) 569 { 570 Object o = null; 571 572 String clsName = type != null && type.getName() != null ? 573 Util.xmlNameToClassName(namespaceURI, type.getName(), true) : 574 Util.xmlNameToClassName(namespaceURI, localName, true); 575 576 Class cls = null; 577 try 578 { 579 cls = Thread.currentThread().getContextClassLoader().loadClass(clsName); 580 } 581 catch(ClassNotFoundException e) 582 { 583 if(log.isTraceEnabled()) 584 { 585 log.trace("create: failed to load class " + clsName); 586 } 587 } 588 589 if(cls != null) 590 { 591 o = newInstance(cls); 592 } 593 594 return o; 595 } 596 597 private static Object get(Object o, String localName, Method getter) 598 { 599 if(log.isTraceEnabled()) 600 { 601 log.trace("get object=" + o + " localName=" + localName + " getter=" + getter); 602 } 603 604 Object value; 605 if(o instanceof ImmutableContainer) 606 { 607 ImmutableContainer con = ((ImmutableContainer)o); 608 value = con.getChild(localName); 609 } 610 else 611 { 612 try 613 { 614 value = getter.invoke(o, null); 615 } 616 catch(Exception e) 617 { 618 throw new NestedRuntimeException("Failed to invoke " + getter + " on " + o, e); 619 } 620 } 621 return value; 622 } 623 624 private static void set(Object parent, Object child, String localName, Method setter) 625 { 626 if(log.isTraceEnabled()) 627 { 628 log.trace("set parent=" + parent + " child=" + child + " localName=" + localName + " setter=" + setter); 629 } 630 631 if(setter != null) 632 { 633 try 634 { 635 setter.invoke(parent, new Object []{child}); 636 } 637 catch(Exception e) 638 { 639 throw new NestedRuntimeException("Failed to set attribute value " + 640 child + 641 " with setter " + 642 setter 643 + " on " + parent + ": ", e 644 ); 645 } 646 } 647 else if(parent instanceof ImmutableContainer) 648 { 649 ((ImmutableContainer)parent).addChild(localName, child); 650 } 651 else 652 { 653 throw new IllegalStateException ("setter is null and it's not an immutable container: parent=" + 654 parent.getClass() + 655 ", localName" + localName + ", parent=" + parent + ", child=" + child 656 ); 657 } 658 } 659 660 private static Object newInstance(Class cls) 661 { 662 if(log.isTraceEnabled()) 663 { 664 log.trace("new " + cls.getName()); 665 } 666 667 Object instance; 668 try 669 { 670 Constructor ctor = cls.getConstructor(null); 671 instance = ctor.newInstance(null); 672 } 673 catch(NoSuchMethodException e) 674 { 675 log.warn("No no-arg constructor in " + cls); 676 instance = new ImmutableContainer(cls); 677 } 678 catch(Exception e) 679 { 680 throw new IllegalStateException ("Failed to create an instance of " + 681 cls + 682 " with the no-arg constructor: " 683 + e.getMessage() 684 ); 685 } 686 return instance; 687 } 688 689 691 private class ElementToClassMapping 692 { 693 public final String element; 694 695 public final Class cls; 696 697 public ElementToClassMapping(String element, Class cls) 698 { 699 this.element = element; 700 this.cls = cls; 701 } 702 703 public String toString() 704 { 705 StringBuffer buffer = new StringBuffer (); 706 buffer.append("ElementToClass@").append(System.identityHashCode(this)); 707 buffer.append("{element=").append(element); 708 if(cls != null) 709 { 710 buffer.append(" class=").append(cls.getName()); 711 } 712 buffer.append("}"); 713 return buffer.toString(); 714 } 715 716 public boolean equals(Object o) 717 { 718 if(this == o) 719 { 720 return true; 721 } 722 if(!(o instanceof ElementToClassMapping)) 723 { 724 return false; 725 } 726 727 final ElementToClassMapping classMapping = (ElementToClassMapping)o; 728 729 if(cls != null ? !cls.equals(classMapping.cls) : classMapping.cls != null) 730 { 731 return false; 732 } 733 734 return true; 735 } 736 737 public int hashCode() 738 { 739 return (cls != null ? cls.hashCode() : 0); 740 } 741 } 742 743 private class ElementToFieldMappingKey 744 { 745 public final String element; 746 747 public final Class cls; 748 749 public ElementToFieldMappingKey(String element, Class cls) 750 { 751 this.element = element; 752 this.cls = cls; 753 } 754 755 public boolean equals(Object o) 756 { 757 if(this == o) 758 { 759 return true; 760 } 761 if(!(o instanceof ElementToFieldMappingKey)) 762 { 763 return false; 764 } 765 766 final ElementToFieldMappingKey elementToFieldMappingKey = (ElementToFieldMappingKey)o; 767 768 if(cls != null ? !cls.equals(elementToFieldMappingKey.cls) : elementToFieldMappingKey.cls != null) 769 { 770 return false; 771 } 772 if(element != null ? 773 !element.equals(elementToFieldMappingKey.element) : 774 elementToFieldMappingKey.element != null) 775 { 776 return false; 777 } 778 779 return true; 780 } 781 782 public int hashCode() 783 { 784 int result; 785 result = (element != null ? element.hashCode() : 0); 786 result = 29 * result + (cls != null ? cls.hashCode() : 0); 787 return result; 788 } 789 } 790 791 private class ElementToFieldMapping 792 { 793 public final String element; 794 795 public final Class cls; 796 797 public final String field; 798 799 public final TypeConverter converter; 800 801 public final ElementToFieldMappingKey key; 802 803 public final Method getter; 804 805 public final Method setter; 806 807 public ElementToFieldMapping(String element, Class cls, String field, TypeConverter converter) 808 { 809 this.element = element; 810 this.cls = cls; 811 this.field = field; 812 this.converter = converter; 813 key = new ElementToFieldMappingKey(element, cls); 814 815 try 816 { 817 getter = Classes.getAttributeGetter(cls, field); 818 } 819 catch(NoSuchMethodException e) 820 { 821 throw new IllegalStateException ("Getter not found for " + field + " in class " + cls.getName()); 822 } 823 824 try 825 { 826 setter = Classes.getAttributeSetter(cls, field, getter.getReturnType()); 827 } 828 catch(NoSuchMethodException e) 829 { 830 throw new IllegalStateException ("Setter not found for " + field + " in class " + cls.getName()); 831 } 832 } 833 834 public String toString() 835 { 836 StringBuffer buffer = new StringBuffer (); 837 buffer.append("ElementToField@").append(System.identityHashCode(this)); 838 buffer.append("{element=").append(element); 839 if(cls != null) 840 { 841 buffer.append(" class=").append(cls.getName()); 842 } 843 buffer.append(" field=").append(field); 844 buffer.append(" getter=").append(getter); 845 buffer.append(" setter=").append(setter); 846 if(converter != null) 847 { 848 buffer.append(" convertor=").append(converter.getClass().getName()); 849 } 850 buffer.append("}"); 851 return buffer.toString(); 852 } 853 854 public boolean equals(Object o) 855 { 856 if(this == o) 857 { 858 return true; 859 } 860 if(!(o instanceof ElementToFieldMapping)) 861 { 862 return false; 863 } 864 865 final ElementToFieldMapping elementToFieldMapping = (ElementToFieldMapping)o; 866 867 if(cls != null ? !cls.equals(elementToFieldMapping.cls) : elementToFieldMapping.cls != null) 868 { 869 return false; 870 } 871 if(element != null ? !element.equals(elementToFieldMapping.element) : elementToFieldMapping.element != null) 872 { 873 return false; 874 } 875 if(field != null ? !field.equals(elementToFieldMapping.field) : elementToFieldMapping.field != null) 876 { 877 return false; 878 } 879 880 return true; 881 } 882 883 public int hashCode() 884 { 885 int result; 886 result = (element != null ? element.hashCode() : 0); 887 result = 29 * result + (cls != null ? cls.hashCode() : 0); 888 result = 29 * result + (field != null ? field.hashCode() : 0); 889 return result; 890 } 891 } 892 893 private static class ImmutableContainer 894 { 895 private final Class cls; 896 897 private final List names = new ArrayList (); 898 899 private final List values = new ArrayList (); 900 901 public ImmutableContainer(Class cls) 902 { 903 this.cls = cls; 904 if(log.isTraceEnabled()) 905 { 906 log.trace("created immutable container for " + cls); 907 } 908 } 909 910 public void addChild(String localName, Object child) 911 { 912 if(!names.isEmpty() && names.get(names.size() - 1).equals(localName)) 913 { 914 throw new IllegalStateException ("Attempt to add duplicate element " + localName); 915 } 916 names.add(localName); 917 values.add(child); 918 919 if(log.isTraceEnabled()) 920 { 921 log.trace("added child " + localName + " for " + cls + ": " + child); 922 } 923 } 924 925 public Object getChild(String localName) 926 { 927 return names.get(names.size() - 1).equals(localName) ? values.get(values.size() - 1) : null; 928 } 929 930 public Object [] getValues() 931 { 932 return values.toArray(); 933 } 934 935 public Class [] getValueTypes() 936 { 937 Class [] types = new Class [values.size()]; 938 for(int i = 0; i < values.size(); ++i) 939 { 940 types[i] = values.get(i).getClass(); 941 } 942 return types; 943 } 944 945 public Object newInstance() 946 { 947 Constructor ctor = null; 948 Constructor [] ctors = cls.getConstructors(); 949 950 if(ctors == null || ctors.length == 0) 951 { 952 throw new JBossXBRuntimeException("The class has no declared constructors: " + cls); 953 } 954 955 for(int i = 0; i < ctors.length; ++i) 956 { 957 Class [] types = ctors[i].getParameterTypes(); 958 959 if(types == null || types.length == 0) 960 { 961 throw new IllegalStateException ("Found no-arg constructor for immutable " + cls); 962 } 963 964 if(types.length == values.size()) 965 { 966 ctor = ctors[i]; 967 968 int typeInd = 0; 969 while(typeInd < types.length) 970 { 971 if(!types[typeInd].isAssignableFrom(values.get(typeInd++).getClass())) 972 { 973 ctor = null; 974 break; 975 } 976 } 977 978 if(ctor != null) 979 { 980 break; 981 } 982 } 983 } 984 985 if(ctor == null) 986 { 987 throw new IllegalStateException ("No constructor in " + cls + " that would take arguments " + values); 988 } 989 990 try 991 { 992 return ctor.newInstance(values.toArray()); 993 } 994 catch(Exception e) 995 { 996 throw new IllegalStateException ("Failed to create immutable instance of " + 997 cls + 998 " using arguments: " 999 + values + ": " + e.getMessage() 1000 ); 1001 } 1002 } 1003 } 1004} 1005 | Popular Tags |