1 56 57 package org.jdom.contrib.beans; 58 59 import java.lang.reflect.*; 60 import java.io.File ; 61 import java.io.IOException ; 62 import java.io.PrintStream ; 63 import java.util.Iterator ; 64 import java.util.*; 65 import java.beans.*; 66 import org.jdom.Document; 67 import org.jdom.Element; 68 import org.jdom.Attribute; 69 import org.jdom.Namespace; 70 import org.jdom.JDOMException; 71 import org.jdom.input.SAXBuilder; 72 import org.jdom.output.XMLOutputter; 73 74 173 174 public class BeanMapper { 175 176 protected String beanPackage; 177 protected Namespace namespace; 178 protected boolean ignoreMissingProperties = false; 179 protected boolean ignoreNullProperties = true; 180 protected List mappings = new ArrayList(); 181 protected StringConverter stringconverter = new StringConverter(); 182 183 188 public BeanMapper() 189 { 190 } 191 192 194 198 public void setBeanPackage(String beanPackage) 199 { 200 this.beanPackage = beanPackage; 201 } 202 203 207 public void setNamespace(Namespace namespace) 208 { 209 this.namespace = namespace; 210 } 211 212 220 public StringConverter getStringConverter() { 221 return stringconverter; 222 } 223 224 227 public void setStringConverter(StringConverter stringconverter) { 228 this.stringconverter = stringconverter; 229 } 230 231 237 public void setIgnoreNullProperties(boolean b) { 238 ignoreNullProperties = b; 239 } 240 241 247 public void setIgnoreMissingProperties(boolean b) { 248 ignoreMissingProperties = b; 249 } 250 251 252 254 258 public Document toDocument(Object bean) throws BeanMapperException { 259 return toDocument(bean, null); 260 } 261 262 267 public Document toDocument(Object bean, String elementName) 268 throws BeanMapperException { 269 Element root = toElement(bean, elementName); 270 Document doc = new Document(root); 271 return doc; 272 } 273 274 279 public Element toElement(Object bean) throws BeanMapperException 280 { 281 return toElement(bean, null); 282 } 283 284 289 public Element toElement(Object bean, String elementName) 290 throws BeanMapperException { 291 BeanInfo info; 292 try { 293 info = Introspector.getBeanInfo(bean.getClass()); 295 } 296 catch (IntrospectionException e) { 297 throw new BeanMapperException("Mapping bean " + bean, e); 298 } 299 300 Element element; 302 String beanname; 303 if (elementName != null) { 304 element = createElement(elementName); 305 } 306 else { 307 Class beanclass = info.getBeanDescriptor().getBeanClass(); 308 beanname = unpackage(beanclass.getName()); 309 element = createElement(beanname); 310 } 311 312 PropertyDescriptor[] properties = info.getPropertyDescriptors(); 314 for (int i=0; i<properties.length; ++i) { 315 PropertyDescriptor prop = properties[i]; 316 String propertyName = prop.getName(); 317 318 Method method = prop.getReadMethod(); 319 320 if (method.getName().equals("getClass") && 322 prop.getPropertyType().getName().equals("java.lang.Class")) 323 continue; 324 325 if (isIgnoredProperty(propertyName)) 327 continue; 328 329 Mapping mapping = getMappingForProperty(propertyName); 331 332 if (method.getParameterTypes().length != 0) 334 continue; 336 337 Object valueObject = null; 338 try { 339 Object [] args = new Object [0]; valueObject = method.invoke(bean, args); 341 } 342 catch (java.lang.IllegalAccessException e) { 343 throw new BeanMapperException("Mapping " + propertyName, e); 344 } 345 catch (java.lang.reflect.InvocationTargetException e) { 346 throw new BeanMapperException("Mapping " + propertyName, e); 347 } 348 349 Object value = convertValue(valueObject); 351 352 if (value == null && ignoreNullProperties) { 353 debug("Ignoring null " + propertyName); 354 continue; 355 } 356 357 String childElementName; 358 359 if (mapping == null) { 360 childElementName = propertyName; 361 } 362 else 363 childElementName = mapping.element; 364 365 367 372 Element child = null; 373 if (childElementName != null) { 374 if (namespace != null) 375 child = element.getChild(childElementName, namespace); 376 else { 377 child = element.getChild(childElementName); 378 } 379 if (child == null) { 380 child = createElement(childElementName); 381 element.addContent(child); 382 } 383 } 384 385 if (mapping == null || mapping.attribute == null) { 386 setElementValue(propertyName, childElementName, 388 element, child, value); 389 } 390 391 else { 392 try { 393 if (value == null) 395 value = ""; if (childElementName == null) element.setAttribute(mapping.attribute, (String )value); 398 else child.setAttribute(mapping.attribute, (String )value); 400 } 401 catch (ClassCastException e) { 402 throw new BeanMapperException( 403 "Can't set type " + value.getClass() + " as attribute"); 404 } 405 } 406 } 407 408 return element; 410 } 412 416 protected Object convertValue(Object value) throws BeanMapperException 417 { 418 if (value == null) 419 return null; 420 Object result; 421 Class type = value.getClass(); 422 String classname = type.getName(); 423 424 if (classname.startsWith("java.lang.") || 427 classname.equals("java.util.Date") 428 ) 429 { 430 result = value.toString(); 431 } 432 else if (type.isArray()) { 433 List list = new ArrayList(); 436 for (int i=0; i<Array.getLength(value); ++i) { 437 Object item = Array.get(value, i); 438 list.add( convertValue(item) ); } 440 result = list; 441 } 442 else 443 { 444 debug("Recurse on bean " + classname + "=" + value); 447 result = toElement(value); 448 } 449 450 return result; 451 } 453 protected void setElementValue(String propertyName, 454 String elementName, 455 Element parent, 456 Element child, 457 Object value) throws BeanMapperException { 458 debug("setElementValue(" + propertyName + "," + 459 elementName + "," + 460 child.getName() + "," + 461 value + ")"); 462 463 if (value == null) 464 ; else if (value instanceof Element) { 466 child.addContent((Element)value); 467 } 468 else if (value instanceof String ) { 469 child.setText((String )value); 470 } 471 else if (value instanceof List) { 472 for (Iterator it = ((List)value).iterator(); 473 it.hasNext(); ) 474 { 475 Object item = it.next(); 476 if (child == null) { 477 child = createElement(elementName); 478 parent.addContent(child); 479 } 480 setElementValue(propertyName, elementName, parent, child, item); 481 child = null; 483 } 484 } 485 else 486 throw new BeanMapperException( 487 "Unknown result type for property " + propertyName + ": " + value); 488 } 489 490 492 496 public Object toBean(Document document) throws BeanMapperException { 497 return toBean(document.getRootElement()); 498 } 499 500 public Object toBean(Element element) throws BeanMapperException { 501 502 Object bean = instantiateBean(element.getName()); 503 504 Iterator i; 505 Mapping mapping; 506 String propertyName; 507 List attributes; 508 List children; 509 510 Set alreadySet = new HashSet(); 511 512 attributes = element.getAttributes(); 514 for (i=attributes.iterator(); i.hasNext(); ) { 515 Attribute attribute = (Attribute)i.next(); 516 debug("Mapping " + attribute); 517 mapping = getMappingForAttribute(null, attribute.getName()); 518 propertyName = (mapping==null) ? 519 attribute.getName() : mapping.property; 520 setProperty(bean, propertyName, attribute.getValue()); 521 } 522 523 children = element.getChildren(); 525 debug(element.toString() + " has " + children.size() + " children"); 526 for (i=children.iterator(); i.hasNext(); ) { 527 Element child = (Element)i.next(); 528 debug("Mapping " + child); 529 530 mapping = getMappingForElement(child.getName()); 531 propertyName = (mapping==null) ? child.getName() : mapping.property; 532 533 PropertyDescriptor property = 535 findPropertyDescriptor(bean, propertyName); 536 if (property != null) { 537 if (!alreadySet.contains(child.getName())) { 538 if (property.getPropertyType().isArray()) 539 setProperty(bean, property, element, child); 540 else 541 setProperty(bean, property, element, child); 542 } 543 } 544 545 attributes = child.getAttributes(); 547 for (Iterator iatt=attributes.iterator(); iatt.hasNext(); ) { 548 Attribute attribute = (Attribute)iatt.next(); 549 debug("Mapping " + attribute); 550 mapping = getMappingForAttribute(child.getName(), 551 attribute.getName()); 552 propertyName = (mapping==null) ? 553 attribute.getName() : mapping.property; 554 setProperty(bean, propertyName, attribute.getValue()); 555 } 557 alreadySet.add(child.getName()); 558 559 } 561 return bean; 562 } 564 565 570 protected Object instantiateBean(String elementName) 571 throws BeanMapperException { 572 String className = null; 574 Class beanClass; 575 try { 576 Mapping mapping = getMappingForElement(elementName); 577 if (mapping != null && 578 mapping.type != null) { 579 beanClass = mapping.type; 580 } 581 else { 582 className = getBeanClassName(beanPackage, elementName); 583 beanClass = Class.forName(className); 584 } 585 Object bean = beanClass.newInstance(); 586 return bean; 587 } 588 catch (ClassNotFoundException e) { 589 throw new BeanMapperException("Class " + className + 590 " not found instantiating " + elementName + 591 " - maybe you need to add a mapping, or add a bean package", e); 592 } 593 catch (Exception e) { 594 throw new BeanMapperException("Instantiating " + elementName, e); 595 } 596 } 597 598 protected String getBeanClassName(String beanPackage, String elementName) { 599 return (beanPackage == null ? "" : (beanPackage + ".")) + 600 Character.toUpperCase(elementName.charAt(0)) + 601 elementName.substring(1); 602 } 603 604 protected PropertyDescriptor findPropertyDescriptor(Object bean, 605 String propertyName) 606 throws BeanMapperException { 607 try { 608 BeanInfo info = Introspector.getBeanInfo(bean.getClass()); 610 PropertyDescriptor[] properties = info.getPropertyDescriptors(); 611 for (int i=0; i<properties.length; ++i) { 612 PropertyDescriptor prop = properties[i]; 613 if (prop.getName().equals(propertyName)) 614 return prop; 615 } 616 } 617 catch (Exception e) { 618 throw new BeanMapperException("Finding property " + propertyName + 619 " for bean " + bean.getClass(), e); 620 } 621 if (ignoreMissingProperties) { 622 return null; 623 } 624 else { 625 throw new BeanMapperException("Missing property: " + 626 propertyName + " in bean " + bean.getClass() + ": " + bean); 627 } 628 } 630 631 638 protected boolean setProperty(Object bean, String propertyName, 639 Object value) throws BeanMapperException { 640 return setProperty(bean, findPropertyDescriptor(bean, propertyName), 641 null, value); 642 } 643 644 651 protected boolean setProperty(Object bean, PropertyDescriptor property, 652 Element parent, Object value) 653 throws BeanMapperException { 654 debug("setProperty: bean=" + bean + " property=" + 655 property.getName() + " value=" + value); 656 try { 657 if (property == null) 658 return false; 659 660 Object valueObject; 662 if (property.getPropertyType().isArray()) { 663 Element child = (Element)value; 665 List children = parent.getChildren(child.getName()); 666 valueObject = buildArray(property, children); 667 } 668 else { 669 valueObject = convertJDOMValue(value, 671 property.getPropertyType()); 672 } 673 674 String propertyName = property.getName(); 676 Method setter = property.getWriteMethod(); 677 Class [] params = setter.getParameterTypes(); 678 if (params.length > 1) 679 throw new BeanMapperException( 680 "Setter takes multiple parameters: " + bean.getClass() + 681 "." + setter.getName()); 682 683 Class param = params[0]; 684 if (param != property.getPropertyType()) 685 debug("Weird: setter takes " + param + ", property is " + 686 property.getPropertyType()); 687 688 debug("Invoking setter: " + setter.getName() + 689 "(" + valueObject + ")"); 690 setter.invoke(bean, new Object [] { valueObject }); 691 692 return true; 693 } 694 catch (BeanMapperException e) { 695 throw e; 696 } 697 catch (Exception e) { 698 throw new BeanMapperException("Setting property " + 699 property.getName() + "=" + value + " in " + bean.getClass(), e); 700 } 701 } 702 703 protected Object convertString(String value, Class type) { 704 if (value == null) 705 return null; 706 if (type == String .class) 707 return value; 708 else 709 return 710 stringconverter.parse((String )value, type); 711 } 712 713 protected Object convertJDOMValue(Object value, Class type) 714 throws BeanMapperException { 715 Object valueObject; 716 717 if (value == null) 719 valueObject = null; 720 721 else if (value instanceof String ) { 723 valueObject = convertString((String )value, type); 724 } 725 726 else if (value instanceof Element) { 728 Element element = (Element)value; 729 730 if (type == Element.class) 732 valueObject = value; 733 734 else if (element.getChildren().isEmpty()) 736 { 737 valueObject = convertString(element.getText(), type); 738 } 739 740 else if (element.getChildren().size() == 1) { 742 743 746 750 valueObject = toBean((Element)element.getChildren().get(0)); 751 } 752 else { 753 throw new BeanMapperException( 756 "Mapping of multiple child elements not implemented for " + 757 element.getName()); 758 } 759 } 760 else { 761 throw new BeanMapperException("Can't map unknown type: " + 762 value.getClass() + "=" + value); 763 } 764 return valueObject; 765 } 767 770 protected Object buildArray(PropertyDescriptor property, List children) 771 throws BeanMapperException { 772 Class arrayClass = property.getPropertyType(); 773 774 Class itemClass; 775 itemClass = arrayClass.getComponentType(); 776 777 if (itemClass == null) { 778 throw new BeanMapperException("Can't instantiate array of type " + 779 arrayClass); 780 } 781 782 Object array = Array.newInstance(itemClass, children.size()); 784 785 for (int i = 0; i<children.size(); ++i) { 787 Element child = (Element)children.get(i); 788 Object value = convertJDOMValue(child, itemClass); 789 debug( itemClass + "[" + i + "]=" + value ); 790 Array.set(array, i, value); 791 } 792 793 return array; 794 } 795 796 797 public static Class getArrayItemClass(Class arrayClass) 798 throws ClassNotFoundException { 799 Class itemClass; 800 801 String arrayClassName = arrayClass.getName(); 804 debug("Parsing array classname: " + arrayClassName); 805 if (arrayClassName.equals("[B")) { 806 itemClass = byte.class; 807 } 808 else if (arrayClassName.equals("[C")) { 809 itemClass = char.class; 810 } 811 else if (arrayClassName.equals("[D")) { 812 itemClass = double.class; 813 } 814 else if (arrayClassName.equals("[F")) { 815 itemClass = float.class; 816 } 817 else if (arrayClassName.equals("[I")) { 818 itemClass = int.class; 819 } 820 else if (arrayClassName.equals("[J")) { 821 itemClass = long.class; 822 } 823 else if (arrayClassName.equals("[S")) { 824 itemClass = short.class; 825 } 826 else if (arrayClassName.equals("[Z")) { 827 itemClass = boolean.class; 828 } 829 else if (arrayClassName.startsWith("[L")) { 830 itemClass = Class.forName( 831 arrayClassName.substring(2, arrayClassName.length()-1)); 832 } 833 else 834 itemClass = null; 835 return itemClass; 836 } 837 838 840 845 public void addMapping(String property, String element) { 846 addMapping(new Mapping(property, null, element, null)); 847 } 848 849 860 public void addMapping(String property, String element, String attribute) { 861 addMapping(new Mapping(property, null, element, attribute)); 862 } 863 864 877 public void addMapping(String property, Class type, 878 String element, String attribute) { 879 addMapping(new Mapping(property, type, element, attribute)); 880 } 881 882 public void addMapping(Mapping mapping) { 883 mappings.add(mapping); 884 } 885 886 public Mapping getMappingForProperty(String property) { 887 Iterator i = mappings.iterator(); 888 while (i.hasNext()) { 889 Mapping m = (Mapping)i.next(); 890 if (m.property != null && m.property.equals(property)) { 891 return m; 892 } 893 } 894 return null; 895 } 896 897 public Mapping getMappingForElement(String element) { 898 Iterator i = mappings.iterator(); 899 while (i.hasNext()) { 900 Mapping m = (Mapping)i.next(); 901 if (m.element.equals(element)) { 902 return m; 903 } 904 } 905 return null; 906 } 907 908 public Mapping getMappingForAttribute(String element, String attribute) { 909 Iterator i = mappings.iterator(); 910 while (i.hasNext()) { 911 Mapping m = (Mapping)i.next(); 912 if (m.element != null && 913 m.attribute != null && 914 m.element.equals(element) && 915 m.attribute.equals(attribute)) 916 { 917 return m; 918 } 919 } 920 return null; 921 } 922 923 public class Mapping { 924 public String property; 925 public Class type; 926 public String element; 927 public String attribute; 928 929 938 public Mapping(String property, Class type, 939 String element, String attribute) { 940 this.property = property; 941 this.type = type; 942 this.element = element; 943 this.attribute = attribute; 944 } 945 946 947 } 948 949 951 protected Set ignoredProperties = new HashSet(); 952 protected Set ignoredElements = new HashSet(); 953 protected Set ignoredAttributes = new HashSet(); 954 955 public void ignoreProperty(String property) { 956 ignoredProperties.add(property); 957 } 958 959 public boolean isIgnoredProperty(String property) { 960 return ignoredProperties.contains(property); 961 } 962 963 public void ignoreElement(String element) { 964 ignoredElements.add(element); 965 } 966 967 public boolean isIgnoredElement(String element) { 968 return ignoredElements.contains(element); 969 } 970 971 static protected String toAttributeString(String element, 972 String attribute) { 973 return (element == null ? "." : element) + 974 "/@" + attribute; 975 } 976 977 public void ignoreAttribute(String element, String attribute) { 978 ignoredAttributes.add(toAttributeString(element, attribute)); 979 } 980 981 public boolean isIgnoredAttribute(String element, String attribute) { 982 return ignoredAttributes.contains( 983 toAttributeString(element, attribute)); 984 } 985 986 988 protected Element createElement(String elementName) { 989 return namespace == null ? new Element(elementName) : 990 new Element(elementName, namespace); 991 } 992 993 protected static String unpackage(String classname) { 994 int dot = Math.max(classname.lastIndexOf("."), 995 classname.lastIndexOf("$")); 996 if (dot > -1) { 997 classname = classname.substring(dot+1); 998 } 999 classname = Introspector.decapitalize(classname); 1000 return classname; 1001 } 1002 1003 public static int debug = 0; 1004 protected static void debug(String msg) { 1005 if (debug > 0) 1006 System.err.println("BeanMapper: " + msg); 1007 } 1008} 1009 1010 | Popular Tags |