1 17 18 package org.sape.carbon.core.config.format; 19 20 import java.beans.BeanInfo ; 21 import java.beans.IntrospectionException ; 22 import java.beans.Introspector ; 23 import java.beans.PropertyDescriptor ; 24 import java.io.Serializable ; 25 import java.lang.reflect.Array ; 26 import java.lang.reflect.Field ; 27 import java.lang.reflect.InvocationTargetException ; 28 import java.lang.reflect.Method ; 29 import java.util.Map ; 30 31 import org.jdom.Document; 32 import org.jdom.Element; 33 34 import org.apache.commons.logging.Log; 35 import org.apache.commons.logging.LogFactory; 36 37 import org.sape.carbon.core.config.Configuration; 38 import org.sape.carbon.core.config.InvalidConfigurationException; 39 import org.sape.carbon.core.exception.InvalidParameterException; 40 import org.sape.carbon.core.util.reflection.GenericProxy; 41 import org.sape.carbon.core.util.string.StringUtil; 42 43 63 public abstract class AbstractConfigurationProxy 64 extends GenericProxy 65 implements Configuration, Serializable { 66 67 70 private Log log = 71 LogFactory.getLog(this.getClass()); 72 73 74 protected static final String REF_PREFIX = "ref://"; 75 76 77 protected static final int REF_PREFIX_LENGTH = REF_PREFIX.length(); 78 79 83 protected Document document; 84 85 90 protected Element element; 91 92 95 protected Class documentType; 96 97 100 protected String name; 101 102 103 private static final String ROOT_TAG = "Configuration"; 104 105 109 private boolean writable = true; 110 111 121 protected AbstractConfigurationProxy( 122 Document document, 123 Element root, 124 Class documentType) { 125 126 this.document = document; 127 this.element = root; 128 this.documentType = documentType; 129 130 this.element.setAttribute( 131 "ConfigurationInterface", 132 this.documentType.getName()); 133 } 134 135 141 protected AbstractConfigurationProxy(Class documentType) { 142 143 this.element = new Element(ROOT_TAG); 144 145 this.documentType = documentType; 146 147 this.element.setAttribute( 148 "ConfigurationInterface", 149 this.documentType.getName()); 150 151 this.document = new Document(this.element); 152 } 153 154 161 public String getConfigurationName() { 162 return this.name; 163 } 164 165 171 public void setConfigurationName(String name) { 172 this.name = name; 173 } 174 175 181 public Class getDocumentType() { 182 return this.documentType; 183 } 184 185 210 protected Object handleInvoke(Object proxy, Method m, Object [] args) 211 throws Throwable { 212 String methodName = m.getName(); 213 Object returnObject = null; 214 215 if (m.getDeclaringClass() == Configuration.class) { 216 try { 219 returnObject = m.invoke(this, args); 220 } catch (InvocationTargetException ite) { 221 throw ite.getTargetException(); 223 } 224 225 } else if (methodName.startsWith("get")) { 226 String attrName = m.getName().substring(3); 227 Class returnType = m.getReturnType(); 228 229 if (returnType.isArray()) { 230 returnObject = 231 getArray(attrName, m.getReturnType().getComponentType()); 232 233 } else if (returnType.equals(Map .class)) { 234 235 236 returnObject = 237 getMap(attrName, getCollectionComponentType(attrName)); 238 } else { 239 240 if ((args == null) || (args.length == 0)) { 241 returnObject = lookupAttribute(attrName, m.getReturnType()); 243 244 } else if (args.length == 1) { 245 if (isMapAttribute(attrName)) { 247 returnObject = 248 getMap(attrName, 249 getCollectionComponentType(attrName)). 250 get(args[0]); 251 252 } else { 253 returnObject = 255 getArrayValue( 256 attrName, 257 m.getReturnType(), 258 ((Integer ) args[0]).intValue()); 259 } 260 } 261 } 262 263 } else if (methodName.startsWith("is")) { 264 String attrName = m.getName().substring(2); 265 returnObject = lookupAttribute(attrName, m.getReturnType()); 266 267 } else if (methodName.startsWith("set")) { 268 if (!this.isConfigurationWritable()) { 270 throw new java.lang.UnsupportedOperationException ( 271 this.getClass().getName() 272 + "Configuration Document is read-only, write " 273 + "not supported."); 274 } 275 276 String attrName = m.getName().substring(3); 277 if (args.length == 1) { 278 alterAttribute(attrName, m.getParameterTypes()[0], args[0]); 280 281 } else if (args.length == 2) { 282 283 if (isMapAttribute(attrName)) { 284 setMapValue( 286 attrName, 287 getCollectionComponentType(attrName), 288 args[0], args[1]); 289 290 } else { 291 setArrayValue( 293 attrName, 294 m.getParameterTypes()[1], 295 ((Integer ) args[0]).intValue(), 296 args[1]); 297 } 298 } 299 300 } else if (methodName.startsWith("add")) { 301 if (!this.isConfigurationWritable()) { 303 throw new java.lang.UnsupportedOperationException ( 304 this.getClass().getName() 305 + "Configuration Document is read-only, write " 306 + "not supported."); 307 } 308 309 String attrName = m.getName().substring(3); 310 addAttribute(attrName, m.getParameterTypes()[0], args[0]); 311 312 } else { 313 throw new UnsupportedOperationException ( 314 this.getClass().getName() 315 + ": Method named [" 316 + methodName 317 + "] in configuration " 318 + "interface [" 319 + m.getDeclaringClass() 320 + "] is not a supported " 321 + "method within a Configuration interface. Methods must " 322 + "conform to the JavaBeans specification."); 323 } 324 325 return returnObject; 326 } 327 328 339 protected Object getArrayValue( 340 String attributeName, Class type, int index) { 341 342 Object array = 343 getArray(attributeName, type); 344 345 Object returnObject = null; 346 try { 347 returnObject = Array.get(array, index); 348 } catch (IndexOutOfBoundsException ioobe) { 349 throw new InvalidParameterException( 350 this.getClass(), 351 "Indexed configuration out of bounds on document [" 352 + this.getConfigurationName() + "] attribute [" 353 + attributeName + "] index [" + index + "]", 354 ioobe); 355 } 356 return returnObject; 357 } 358 359 365 protected boolean isMapAttribute(String attrName) { 366 boolean isMapAttribute = false; 367 368 try { 369 Method readMethod = this.documentType.getMethod( 370 "get" + attrName, new Class [0]); 371 372 isMapAttribute = readMethod.getReturnType().equals(Map .class); 373 } catch (NoSuchMethodException e) { 374 } 376 377 return isMapAttribute; 378 } 379 380 388 protected Class getCollectionComponentType(String attrName) { 389 Class componentType = null; 390 391 try { 392 Method readMethod = this.documentType.getMethod( 393 "get" + attrName, new Class [] { String .class }); 394 395 componentType = readMethod.getReturnType(); 396 } catch (NoSuchMethodException e) { 397 } 399 400 return componentType; 401 } 402 403 409 public Class getChildType(String attributeName) { 410 attributeName = StringUtil.capitalize(attributeName); 411 try { 412 Method method = 413 this.documentType.getMethod( 414 "get" + attributeName, 415 new Class [0]); 416 return method.getReturnType(); 417 } catch (NoSuchMethodException nsme) { 418 throw new InvalidParameterException( 419 this.getClass(), 420 "Unknown attribute [" + attributeName + "] on configuration [" 421 + this.getConfigurationName() + "]"); 422 } 423 } 424 425 436 protected Object lookupDefaultAttributeValue( 437 Class type, 438 String attributeName, 439 Class returnType) { 440 441 try { 442 443 Field field = type.getField(attributeName); 444 Object val = field.get(null); 445 446 if (val == null) { 447 return null; 448 } else { 449 if (field.getType().equals(returnType)) { 450 return val; 452 } else { 453 StringBuffer buf = new StringBuffer (); 455 buf.append("The Default value in the configuration of ["); 456 buf.append(type.getName()); 457 buf.append("] was of type ["); 458 buf.append(field.getType()); 459 buf.append( 460 "], but the configuration interface requires a "); 461 buf.append(" default of type ["); 462 buf.append(returnType); 463 buf.append("]."); 464 465 throw new InvalidConfigurationException( 466 this.getClass(), 467 this.getConfigurationName(), 468 buf.toString()); 469 470 } 471 } 472 } catch (NoSuchFieldException nsfe) { 473 Class [] superInterfaces = type.getInterfaces(); 476 Object val = null; 477 for (int i = 0; 478 ((i < superInterfaces.length) && (val == null)); 479 i++) { 480 481 val = 482 lookupDefaultAttributeValue( 483 superInterfaces[i], 484 attributeName, 485 returnType); 486 } 487 return val; 488 } catch (IllegalAccessException iae) { 489 throw new InvalidConfigurationException( 490 this.getClass(), 491 this.getConfigurationName(), 492 attributeName, 493 "Could not access the default variable named [" 494 + attributeName 495 + "] in the configuration interface [" 496 + type.getName() 497 + "].", 498 iae); 499 } catch (IllegalArgumentException iae) { 500 throw new InvalidConfigurationException( 501 this.getClass(), 502 this.getConfigurationName(), 503 attributeName, 504 "Could not access the default variable named [" 505 + attributeName 506 + "] in the configuration interface [" 507 + type.getName() 508 + "].", 509 iae); 510 } 511 512 } 513 514 523 public abstract void alterAttribute( 524 String attributeName, 525 Class attributeType, 526 Object newValue); 527 528 540 public abstract Object lookupAttribute( 541 String attributeName, 542 Class returnType); 543 544 545 546 556 public abstract Object getArray(String attributeName, Class componentType); 557 558 568 public abstract Map getMap(String attributeName, Class contentType); 569 570 571 582 public abstract void setArrayValue( 583 String attributeName, 584 Class attributeType, 585 int index, 586 Object value); 587 588 599 public abstract void setMapValue( 600 String attributeName, 601 Class attributeType, 602 Object key, 603 Object value); 604 605 606 615 public abstract Element addAttribute( 616 String entityName, 617 Class type, 618 Object obj); 619 620 627 public static String getSimpleClassName(Class theClass) { 628 String className = theClass.getName(); 629 if (className.lastIndexOf('.') > className.lastIndexOf('$')) { 630 return className.substring(className.lastIndexOf('.') + 1); 631 } else { 632 return className.substring(className.lastIndexOf('$') + 1); 633 } 634 } 635 638 public Class getConfigurationInterface() { 639 try { 640 String configurationInterfaceName = 641 this.element.getAttributeValue("ConfigurationInterface"); 642 643 if (configurationInterfaceName == null) { 644 throw new InvalidConfigurationException( 645 this.getClass(), 646 getConfigurationName(), 647 "ConfigurationInterface", 648 "No value specifed"); 649 } 650 651 return Class.forName(configurationInterfaceName); 652 } catch (ClassNotFoundException cnfe) { 653 throw new InvalidConfigurationException( 654 this.getClass(), 655 getConfigurationName(), 656 "ConfigurationInterface", 657 "Specifed class not found", 658 cnfe); 659 } 660 } 661 662 665 public Document getDataStructure() { 666 return this.document; 667 } 668 669 676 public Element getRootElement() { 677 return this.element; 678 } 679 680 683 public abstract Object clone(); 684 685 691 public boolean isChildConfiguration(Element childElement) { 692 return ( 693 childElement.getAttribute("ConfigurationInterface") != null 694 || isReference(childElement)); 695 } 696 697 703 public boolean isReference(Element element) { 704 String textValue = element.getTextTrim(); 705 return textValue.startsWith(REF_PREFIX); 706 } 707 708 718 protected Class getConfigurationInterface( 719 Element element, 720 Class defaultInterface) { 721 722 Class configurationInterface = defaultInterface; 723 724 try { 725 String configTypeName = 726 element.getAttributeValue("ConfigurationInterface"); 727 728 if (configTypeName != null) { 729 configurationInterface = Class.forName(configTypeName); 730 } 731 732 733 if (!Configuration.class.isAssignableFrom(configurationInterface)) { 734 throw new InvalidConfigurationException( 735 this.getClass(), 736 this.getConfigurationName(), 737 this.element.getName(), 738 "The configured configuration interface does not extend " 739 + "[org.sape.carbon.core.config.Configuration] and " 740 + "therefore can not be instantiated. Please correct " 741 + "the class [" 742 + configurationInterface.getName() + "]"); 743 } 744 745 } catch (ClassNotFoundException cnfe) { 746 throw new InvalidConfigurationException( 747 this.getClass(), 748 this.getConfigurationName(), 749 element.getName(), 750 "The child configuration has defined a configuration interface " + 751 "class which can not be found. Check the configuration document " + 752 "to ensure the class name is correct.", 753 cnfe); 754 } catch (ExceptionInInitializerError eiie) { 755 if (log.isTraceEnabled()) { 756 log.trace("Caught ExceptionInInitializerError [" 757 + eiie 758 + "], returning default interface"); 759 } 760 } 761 762 return configurationInterface; 763 } 764 767 protected Boolean proxyEquals(Object proxy, Object other) { 768 try { 769 Element proxyElement = ((Configuration) proxy).getRootElement(); 770 Element otherElement = ((Configuration) other).getRootElement(); 771 772 if (proxyElement == otherElement) { 773 return Boolean.TRUE; 774 } else { 775 return Boolean.FALSE; 776 } 777 } catch (ClassCastException cce) { 778 return Boolean.FALSE; 779 } 780 } 781 782 785 protected Integer proxyHashCode(Object proxy) { 786 Element proxyElement = ((Configuration) proxy).getRootElement(); 787 return new Integer (System.identityHashCode(proxyElement)); 788 } 789 790 799 public boolean isConfigurationWritable() { 800 return this.writable; 801 } 802 803 807 public void setConfigurationReadOnly() { 808 this.writable = false; 809 } 810 811 } | Popular Tags |