1 17 18 package org.sape.carbon.core.config.format.jdom; 19 20 21 import java.beans.IndexedPropertyDescriptor ; 22 import java.beans.IntrospectionException ; 23 import java.beans.Introspector ; 24 import java.beans.PropertyDescriptor ; 25 import java.lang.reflect.Array ; 26 import java.lang.reflect.Proxy ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 32 import org.sape.carbon.core.config.Config; 33 import org.sape.carbon.core.config.Configuration; 34 import org.sape.carbon.core.config.InvalidConfigurationException; 35 import org.sape.carbon.core.config.format.AbstractConfigurationProxy; 36 import org.sape.carbon.core.config.format.ConfigurationFormatException; 37 import org.sape.carbon.core.config.node.Node; 38 import org.sape.carbon.core.config.type.ConfigurationTypeException; 39 import org.sape.carbon.core.config.type.ConfigurationTypeService; 40 import org.sape.carbon.core.config.type.ConfigurationTypeServiceFactory; 41 import org.sape.carbon.core.exception.IllegalStateException; 42 import org.sape.carbon.core.exception.InvalidParameterException; 43 import org.sape.carbon.core.util.string.StringUtil; 44 45 import org.jdom.Document; 46 import org.jdom.Element; 47 48 59 public class JDOMConfigurationProxy extends AbstractConfigurationProxy { 60 61 protected final ConfigurationTypeService typeService = 62 ConfigurationTypeServiceFactory.getInstance(); 63 64 68 protected Map attributeCache = new HashMap (); 69 70 71 public static final String MAP_KEY_ATTRIBUTE = "Key"; 72 73 80 public JDOMConfigurationProxy(Class configurationClass) { 81 super(configurationClass); 82 } 83 84 94 public JDOMConfigurationProxy( 95 Document document, 96 Class configurationClass) { 97 98 super(document, document.getRootElement(), configurationClass); 99 } 100 101 113 public JDOMConfigurationProxy(Element element, Class configurationClass) { 114 115 super(null, element, configurationClass); 116 } 117 118 127 public String proxyToString(Object proxy) { 128 return this.getDocumentType().getName() 129 + " [" 130 + this.getConfigurationName() 131 + "]"; 132 } 133 134 141 public Object clone() { 142 143 JDOMConfigurationProxy proxy = null; 144 145 if (this.document == null) { 146 Element newElement = (Element) super.element.clone(); 148 149 proxy = 154 new JDOMConfigurationProxy(newElement, this.getDocumentType()); 155 156 } else { 157 Document doc = (Document) this.document.clone(); 158 159 proxy = 164 new JDOMConfigurationProxy(doc, this.getDocumentType()); 165 } 166 167 proxy.setConfigurationName(this.getConfigurationName()); 168 169 Configuration config = 170 (Configuration) Proxy.newProxyInstance( 171 this.getClass().getClassLoader(), 172 new Class [] {this.getDocumentType()}, 173 proxy); 174 175 return config; 176 } 177 178 182 public Object lookupAttribute(String attributeName, Class returnType) { 183 Object attributeValue = this.attributeCache.get(attributeName); 184 if (attributeValue == null) { 185 186 try { 187 Element childElement = 189 this.element.getChild(attributeName); 190 191 attributeValue = 193 formatElement(childElement, attributeName, returnType); 194 195 if(this.typeService.isCacheableType(returnType)) { 196 this.attributeCache.put(attributeName, attributeValue); 197 } 198 199 } catch (ConfigurationFormatException cfe) { 200 throw new InvalidConfigurationException( 201 this.getClass(), 202 this.getConfigurationName(), 203 attributeName, 204 "Format error on configuration value.", 205 cfe); 206 } 207 } 208 209 return attributeValue; 210 } 211 212 213 223 public Object getArray(String attributeName, Class componentType) { 224 225 Object attributeArray = this.attributeCache.get(attributeName); 226 if (attributeArray == null) { 227 Element collectionElement = 228 getCollectionElement(attributeName, Array .class); 229 230 List list = collectionElement.getChildren(attributeName); 231 int length = list.size(); 232 233 attributeArray = Array.newInstance(componentType, length); 234 235 try { 236 for (int i = 0; i < length; i++) { 237 Element element = (Element) list.get(i); 239 240 Object attribute = 242 formatContainedElement( 243 element, 244 attributeName, 245 String.valueOf(i), 246 componentType); 247 248 Array.set(attributeArray, i, attribute); 250 } 251 if (this.typeService.isCacheableType(componentType)) { 252 this.attributeCache.put(attributeName, attributeArray); 253 } 254 } catch (ConfigurationFormatException cfe) { 255 throw new InvalidConfigurationException( 256 this.getClass(), 257 this.getConfigurationName(), 258 attributeName, 259 "Format error on configuration value.", 260 cfe); 261 } 262 } 263 return attributeArray; 264 } 265 266 270 public Map getMap(String tagName, Class contentType) { 271 272 Map attributeMap = (Map ) this.attributeCache.get(tagName); 273 if (attributeMap == null) { 274 275 Element collectionElement = 276 getCollectionElement(tagName, Map .class); 277 278 List list = collectionElement.getChildren(tagName); 279 int length = list.size(); 280 281 282 attributeMap = new HashMap (); 283 284 285 try { 286 for (int i = 0; i < length; i++) { 287 Element element = (Element) list.get(i); 289 String unformattedKey = 290 element.getAttributeValue(MAP_KEY_ATTRIBUTE); 291 292 if (unformattedKey == null) { 293 throw new InvalidConfigurationException( 294 this.getClass(), 295 this.getConfigurationName(), 296 tagName, 297 "All map entries in configuration must " 298 + "have a valid [Key] attribute. (And " 299 + "its case sensitive)"); 300 } 301 302 String key = (String ) 310 this.typeService.toObject(String .class, unformattedKey); 311 312 Object attribute = formatContainedElement( 314 element, tagName, key, contentType); 315 316 if (this.typeService.isCacheableType(contentType)) { 318 attributeMap.put(key, attribute); 319 } 320 } 321 } catch (NullPointerException npe) { 322 throw new InvalidConfigurationException( 323 this.getClass(), 324 this.getConfigurationName(), 325 tagName, 326 "Map tags must have keys specified by the " 327 + MAP_KEY_ATTRIBUTE + " attribute.", 328 npe); 329 } catch (ConfigurationFormatException cfe) { 330 331 throw new InvalidConfigurationException( 332 this.getClass(), 333 this.getConfigurationName(), 334 tagName, 335 "Format error on configuration value.", 336 cfe); 337 } catch (ConfigurationTypeException cte) { 338 339 throw new InvalidConfigurationException( 340 this.getClass(), 341 this.getConfigurationName(), 342 tagName, 343 "Format error on configuration key.", 344 cte); 345 } 346 this.attributeCache.put(tagName, attributeMap); 347 } 348 return attributeMap; 349 } 350 354 public void alterAttribute( 355 String attributeName, 356 Class attributeType, 357 Object newValue) { 358 359 this.attributeCache.remove(attributeName); 361 362 if (attributeType.isArray()) { 363 364 Element collectionElement = 366 getCollectionElement(attributeName, attributeType); 367 368 collectionElement.removeChildren(attributeName); 369 370 if (newValue != null) { 371 for (int i = 0; i < Array.getLength(newValue); i++) { 375 Object obj = Array.get(newValue, i); 376 addAttribute( 377 collectionElement, 378 attributeName, 379 attributeType.getComponentType(), 380 obj); 381 } 382 } 383 384 } else if (Map .class.isAssignableFrom(attributeType)) { 385 Element collectionElement = 386 getCollectionElement(attributeName, attributeType); 387 388 collectionElement.removeChildren(attributeName); 389 Map map = (Map ) newValue; 390 for (Iterator mapIterator = map.entrySet().iterator(); 391 mapIterator.hasNext();) { 392 393 394 Map.Entry entry = (Map.Entry ) mapIterator.next(); 395 addAttribute( 396 collectionElement, 397 attributeName, 398 super.getCollectionComponentType(attributeName), 399 entry.getValue()).setAttribute( 400 MAP_KEY_ATTRIBUTE, (String ) entry.getKey()); 401 } 402 } else { 403 Element attribute = this.element.getChild(attributeName); 404 if (attribute != null) { 405 this.element.removeContent(attribute); 407 } 408 if (newValue != null) { 409 addAttribute(this.element, attributeName, attributeType, newValue); 410 } 411 } 412 } 413 414 418 public Element addAttribute(String attributeName, Class type, Object obj) { 419 this.attributeCache.remove(attributeName); 421 422 return addAttribute( 423 getCollectionElement(attributeName, Array .class), 424 attributeName, 425 type, 426 obj); 427 } 428 429 438 protected Element addAttribute( 439 Element element, String attributeName, Class type, Object obj) { 440 441 Element newElement = constructElement(attributeName, type, obj); 443 element.addContent(newElement); 445 return newElement; 446 } 447 448 452 public void setArrayValue( 453 String attributeName, 454 Class attributeType, 455 int index, 456 Object value) { 457 458 this.attributeCache.remove(attributeName); 460 461 Element collectionElement = 462 getCollectionElement(attributeName, Array .class); 463 464 List children = collectionElement.getChildren(attributeName); 465 466 if (index < 0 || index >= children.size()) { 468 throw new InvalidParameterException( 469 this.getClass(), 470 "Index out of bounds: Configuration name [" 471 + this.getConfigurationName() 472 + "], Attribute name [" 473 + attributeName 474 + "], size [" 475 + children.size() 476 + "], requested index [" 477 + index 478 + "]"); 479 } 480 481 if (value == null) { 482 children.remove(index); 484 485 } else { 486 Element newElement = 488 constructElement(attributeName, attributeType, value); 489 children.set(index, newElement); 491 } 492 } 493 494 505 public void setMapValue( 506 String attributeName, 507 Class attributeType, 508 Object key, 509 Object value) { 510 511 this.attributeCache.remove(attributeName); 513 514 Element collectionElement = 515 getCollectionElement(attributeName, Map .class); 516 517 List children = collectionElement.getChildren(attributeName); 518 519 int oldIndex = -1; 520 for (int i = 0; i < children.size(); i++) { 524 Element contentElement = (Element) children.get(i); 525 if (key.equals(contentElement.getAttributeValue(MAP_KEY_ATTRIBUTE))) { 526 oldIndex = i; 527 } 528 } 529 530 531 if (value == null) { 532 } else { 534 Element newElement = 536 constructElement(attributeName, attributeType, value); 537 newElement.setAttribute(MAP_KEY_ATTRIBUTE, (String ) key); 538 if (oldIndex >= 0) { 540 children.set(oldIndex, newElement); 541 } else { 542 children.add(newElement); 543 } 544 } 545 } 546 547 554 protected Element getCollectionElement(String attributeName, Class type) { 555 String tagName = attributeName; 556 if (Map .class.isAssignableFrom(type)) { 557 tagName += "Map"; 558 } else { 559 tagName += "Array"; 560 } 561 562 Element collectionElement = this.element.getChild(tagName); 563 if (collectionElement == null) { 564 collectionElement = new Element(tagName); 565 this.element.addContent(collectionElement); 566 } 567 return collectionElement; 568 } 569 570 592 protected Object formatElement(Element element, String name, Class type) 593 throws ConfigurationFormatException { 594 595 try { 596 Object formattedElement = null; 597 598 if (Configuration.class.isAssignableFrom(type)) { 599 if (element != null) { 601 formattedElement = 602 getChildConfiguration( 603 type, 604 element, 605 getConfigurationName() + Node.DELIMITER + name); 606 } 607 608 } else if (this.typeService.isComplexType(type)) { 609 Configuration subConfiguration = null; 612 613 if (element != null) { 614 subConfiguration = 615 getChildConfiguration( 616 this.typeService.getRequiredConfigurationInterface( 617 type), 618 element, 619 getConfigurationName() + Node.DELIMITER + name); 620 } 621 622 formattedElement = 625 this.typeService.toObject(type, subConfiguration); 626 627 } else { 628 if (element != null) { 630 formattedElement = 631 this.typeService.toObject(type, element.getText()); 632 } 633 if (formattedElement == null) { 636 637 formattedElement = 638 lookupDefaultAttributeValue( 639 this.getConfigurationInterface(), 640 name, 641 type); 642 643 if ((formattedElement == null) && type.isPrimitive()) { 644 throw new InvalidConfigurationException( 647 this.getClass(), 648 this.getConfigurationName(), 649 name, 650 "Primitive configurations must have values as " 651 + "they cannot return null."); 652 } 653 } 654 } 655 656 return formattedElement; 657 658 } catch (ConfigurationTypeException cte) { 659 throw new ConfigurationFormatException( 660 this.getClass(), 661 "Could not parse config data in document [" 662 + getConfigurationName() 663 + "], attribute name [" 664 + name 665 + "], expected type [" 666 + type.getName() 667 + "] data was [" 668 + element.getText() 669 + "]", 670 cte); 671 } 672 } 673 674 691 protected Object formatContainedElement( 692 Element element, 693 String name, 694 String key, 695 Class type) 696 throws ConfigurationFormatException { 697 698 try { 699 Object formattedElement = null; 700 701 if (Configuration.class.isAssignableFrom(type)) { 702 if (element != null) { 704 formattedElement = 705 getChildConfiguration( 706 type, 707 element, 708 getConfigurationName() 709 + Node.DELIMITER 710 + name 711 + "[" 712 + key 713 + "]"); 714 715 } 716 717 } else if (this.typeService.isComplexType(type)) { 718 Configuration subConfiguration = null; 721 722 if (element != null) { 723 subConfiguration = 724 getChildConfiguration( 725 this.typeService.getRequiredConfigurationInterface( 726 type), 727 element, 728 getConfigurationName() 729 + Node.DELIMITER 730 + name 731 + "[" 732 + key 733 + "]"); 734 735 } 736 737 formattedElement = 738 this.typeService.toObject(type, subConfiguration); 739 740 } else { 741 if (element != null) { 742 formattedElement = 743 this.typeService.toObject(type, element.getText()); 744 } 745 if ((formattedElement == null) && type.isPrimitive()) { 746 throw new InvalidConfigurationException( 749 this.getClass(), 750 this.getConfigurationName(), 751 name, 752 "Primitive configurations must have values as " 753 + "they cannot return null."); 754 } 755 } 756 757 return formattedElement; 758 } catch (ConfigurationTypeException cte) { 759 throw new ConfigurationFormatException( 760 this.getClass(), 761 "Could not parse config data in document [" 762 + getConfigurationName() 763 + "], attribute name [" 764 + name 765 + "] index [" 766 + key 767 + "], expected type [" 768 + type.getName() 769 + "] data was [" 770 + element.getText() 771 + "]", 772 cte); 773 } 774 } 775 776 792 protected Configuration getChildConfiguration( 793 Class requiredInterface, 794 Element element, 795 String name) { 796 797 Configuration subConfiguration; 798 799 String textValue = element.getTextTrim(); 800 if (textValue.startsWith(REF_PREFIX)) { 801 if (this.isConfigurationWritable()) { 804 subConfiguration = 805 Config.getInstance().fetchWritableConfiguration( 806 textValue.substring(REF_PREFIX_LENGTH)); 807 } else { 808 subConfiguration = 809 Config.getInstance().fetchConfiguration( 810 textValue.substring(REF_PREFIX_LENGTH)); 811 } 812 } else { 813 814 Class configType = 816 getConfigurationInterface(element, requiredInterface); 817 818 JDOMConfigurationProxy invocationHandler = 819 new JDOMConfigurationProxy(element, configType); 820 invocationHandler.setConfigurationName(name); 821 Object proxy = 822 Proxy.newProxyInstance( 823 configType.getClassLoader(), 824 new Class [] {configType}, 825 invocationHandler); 826 827 subConfiguration = (Configuration) proxy; 828 829 if (!isConfigurationWritable()) { 830 subConfiguration.setConfigurationReadOnly(); 831 } 832 } 833 834 if (!requiredInterface.isAssignableFrom(subConfiguration.getClass())) { 836 837 throw new InvalidConfigurationException( 838 this.getClass(), 839 getConfigurationName(), 840 element.getName(), 841 "Required interface was not assignable from configured " 842 + "interface, required [" 843 + requiredInterface.getName() 844 + "] configured [" 845 + subConfiguration.getConfigurationInterface() 846 + "]"); 847 } 848 849 return subConfiguration; 850 } 851 852 861 protected Element constructElement( 862 String entityName, 863 Class type, 864 Object obj) { 865 866 Element newElement; 867 if (Configuration.class.isAssignableFrom(type)) { 868 newElement = configurationToElement((Configuration) obj); 870 newElement.setName(entityName); 871 872 } else if (this.typeService.isComplexType(type)) { 873 try { 875 Configuration configValue = 876 this.typeService.toConfiguration(type, obj); 877 newElement = configurationToElement(configValue); 878 newElement.setName(entityName); 879 880 } catch (ConfigurationTypeException cte) { 881 throw new InvalidConfigurationException( 882 this.getClass(), 883 getConfigurationName(), 884 entityName, 885 "Could not convert object to configuration", 886 cte); 887 } 888 889 } else { 890 newElement = new Element(entityName); 892 newElement.setText(this.typeService.toString(type, obj)); 893 } 894 return newElement; 895 } 896 897 907 protected Element configurationToElement(Configuration config) { 908 boolean isNested; 909 910 Element configElement; 911 912 if (config.getConfigurationName() == null) { 913 configElement = (Element) config.getRootElement().clone(); 915 916 } else if (getConfigurationName() == null) { 917 configElement = new Element("Configuration"); 919 configElement.setText(REF_PREFIX + config.getConfigurationName()); 920 921 } else if ( 922 config.getConfigurationName().startsWith( 923 getConfigurationName() + Node.DELIMITER)) { 924 configElement = (Element) config.getRootElement().clone(); 926 927 } else { 928 configElement = new Element("Configuration"); 930 configElement.setText(REF_PREFIX + config.getConfigurationName()); 931 } 932 933 return configElement; 934 } 935 936 939 public void setConfigurationReadOnly() { 940 super.setConfigurationReadOnly(); 941 947 preLoadAttributeCache(); 949 } 950 951 972 protected void preLoadAttributeCache() { 973 974 try { 975 Class configInterface = getConfigurationInterface(); 976 977 PropertyDescriptor [] properties = 978 Introspector 979 .getBeanInfo(configInterface) 980 .getPropertyDescriptors(); 981 982 for (int i = 0; i < properties.length; i++) { 983 try { 984 PropertyDescriptor thisProperty = properties[i]; 985 986 String propertyName = 987 StringUtil.capitalize(thisProperty.getName()); 988 989 Class propertyType = thisProperty.getPropertyType(); 990 991 if (thisProperty instanceof IndexedPropertyDescriptor 992 || propertyType.isArray()) { 993 994 getArray(propertyName, propertyType.getComponentType()); 995 996 } else if (Map .class.isAssignableFrom(propertyType)) { 997 getMap( 998 propertyName, 999 getCollectionComponentType(propertyName)); 1000 1001 } else { 1002 lookupAttribute(propertyName, propertyType); 1003 } 1004 1005 1006 1024 } catch (InvalidConfigurationException ice) { 1025 } 1032 } 1033 } catch (IntrospectionException ie) { 1034 throw new IllegalStateException ( 1035 this.getClass(), 1036 "Caught IntrospectionException which should not ever happen", 1037 ie); 1038 } 1039 } 1040 1041} | Popular Tags |