1 16 package org.apache.commons.collections; 17 18 import java.beans.BeanInfo ; 19 import java.beans.IntrospectionException ; 20 import java.beans.Introspector ; 21 import java.beans.PropertyDescriptor ; 22 import java.lang.reflect.Constructor ; 23 import java.lang.reflect.InvocationTargetException ; 24 import java.lang.reflect.Method ; 25 import java.util.AbstractMap ; 26 import java.util.AbstractSet ; 27 import java.util.ArrayList ; 28 import java.util.Collection ; 29 import java.util.HashMap ; 30 import java.util.Iterator ; 31 import java.util.Set ; 32 33 import org.apache.commons.collections.list.UnmodifiableList; 34 import org.apache.commons.collections.keyvalue.AbstractMapEntry; 35 import org.apache.commons.collections.set.UnmodifiableSet; 36 37 50 public class BeanMap extends AbstractMap implements Cloneable { 51 52 private transient Object bean; 53 54 private transient HashMap readMethods = new HashMap (); 55 private transient HashMap writeMethods = new HashMap (); 56 private transient HashMap types = new HashMap (); 57 58 61 public static final Object [] NULL_ARGUMENTS = {}; 62 63 67 public static HashMap defaultTransformers = new HashMap (); 68 69 static { 70 defaultTransformers.put( 71 Boolean.TYPE, 72 new Transformer() { 73 public Object transform( Object input ) { 74 return Boolean.valueOf( input.toString() ); 75 } 76 } 77 ); 78 defaultTransformers.put( 79 Character.TYPE, 80 new Transformer() { 81 public Object transform( Object input ) { 82 return new Character ( input.toString().charAt( 0 ) ); 83 } 84 } 85 ); 86 defaultTransformers.put( 87 Byte.TYPE, 88 new Transformer() { 89 public Object transform( Object input ) { 90 return Byte.valueOf( input.toString() ); 91 } 92 } 93 ); 94 defaultTransformers.put( 95 Short.TYPE, 96 new Transformer() { 97 public Object transform( Object input ) { 98 return Short.valueOf( input.toString() ); 99 } 100 } 101 ); 102 defaultTransformers.put( 103 Integer.TYPE, 104 new Transformer() { 105 public Object transform( Object input ) { 106 return Integer.valueOf( input.toString() ); 107 } 108 } 109 ); 110 defaultTransformers.put( 111 Long.TYPE, 112 new Transformer() { 113 public Object transform( Object input ) { 114 return Long.valueOf( input.toString() ); 115 } 116 } 117 ); 118 defaultTransformers.put( 119 Float.TYPE, 120 new Transformer() { 121 public Object transform( Object input ) { 122 return Float.valueOf( input.toString() ); 123 } 124 } 125 ); 126 defaultTransformers.put( 127 Double.TYPE, 128 new Transformer() { 129 public Object transform( Object input ) { 130 return Double.valueOf( input.toString() ); 131 } 132 } 133 ); 134 } 135 136 137 140 143 public BeanMap() { 144 } 145 146 153 public BeanMap(Object bean) { 154 this.bean = bean; 155 initialise(); 156 } 157 158 161 public String toString() { 162 return "BeanMap<" + String.valueOf(bean) + ">"; 163 } 164 165 188 public Object clone() throws CloneNotSupportedException { 189 BeanMap newMap = (BeanMap)super.clone(); 190 191 if(bean == null) { 192 return newMap; 195 } 196 197 Object newBean = null; 198 Class beanClass = null; 199 try { 200 beanClass = bean.getClass(); 201 newBean = beanClass.newInstance(); 202 } catch (Exception e) { 203 throw new CloneNotSupportedException 205 ("Unable to instantiate the underlying bean \"" + 206 beanClass.getName() + "\": " + e); 207 } 208 209 try { 210 newMap.setBean(newBean); 211 } catch (Exception exception) { 212 throw new CloneNotSupportedException 213 ("Unable to set bean in the cloned bean map: " + 214 exception); 215 } 216 217 try { 218 Iterator readableKeys = readMethods.keySet().iterator(); 222 while(readableKeys.hasNext()) { 223 Object key = readableKeys.next(); 224 if(getWriteMethod(key) != null) { 225 newMap.put(key, get(key)); 226 } 227 } 228 } catch (Exception exception) { 229 throw new CloneNotSupportedException 230 ("Unable to copy bean values to cloned bean map: " + 231 exception); 232 } 233 234 return newMap; 235 } 236 237 243 public void putAllWriteable(BeanMap map) { 244 Iterator readableKeys = map.readMethods.keySet().iterator(); 245 while (readableKeys.hasNext()) { 246 Object key = readableKeys.next(); 247 if (getWriteMethod(key) != null) { 248 this.put(key, map.get(key)); 249 } 250 } 251 } 252 253 254 262 public void clear() { 263 if(bean == null) return; 264 265 Class beanClass = null; 266 try { 267 beanClass = bean.getClass(); 268 bean = beanClass.newInstance(); 269 } 270 catch (Exception e) { 271 throw new UnsupportedOperationException ( "Could not create new instance of class: " + beanClass ); 272 } 273 } 274 275 290 public boolean containsKey(Object name) { 291 Method method = getReadMethod(name); 292 return method != null; 293 } 294 295 303 public boolean containsValue(Object value) { 304 return super.containsValue(value); 306 } 307 308 323 public Object get(Object name) { 324 if ( bean != null ) { 325 Method method = getReadMethod( name ); 326 if ( method != null ) { 327 try { 328 return method.invoke( bean, NULL_ARGUMENTS ); 329 } 330 catch ( IllegalAccessException e ) { 331 logWarn( e ); 332 } 333 catch ( IllegalArgumentException e ) { 334 logWarn( e ); 335 } 336 catch ( InvocationTargetException e ) { 337 logWarn( e ); 338 } 339 catch ( NullPointerException e ) { 340 logWarn( e ); 341 } 342 } 343 } 344 return null; 345 } 346 347 358 public Object put(Object name, Object value) throws IllegalArgumentException , ClassCastException { 359 if ( bean != null ) { 360 Object oldValue = get( name ); 361 Method method = getWriteMethod( name ); 362 if ( method == null ) { 363 throw new IllegalArgumentException ( "The bean of type: "+ bean.getClass().getName() + " has no property called: " + name ); 364 } 365 try { 366 Object [] arguments = createWriteMethodArguments( method, value ); 367 method.invoke( bean, arguments ); 368 369 Object newValue = get( name ); 370 firePropertyChange( name, oldValue, newValue ); 371 } 372 catch ( InvocationTargetException e ) { 373 logInfo( e ); 374 throw new IllegalArgumentException ( e.getMessage() ); 375 } 376 catch ( IllegalAccessException e ) { 377 logInfo( e ); 378 throw new IllegalArgumentException ( e.getMessage() ); 379 } 380 return oldValue; 381 } 382 return null; 383 } 384 385 390 public int size() { 391 return readMethods.size(); 392 } 393 394 395 405 public Set keySet() { 406 return UnmodifiableSet.decorate(readMethods.keySet()); 407 } 408 409 416 public Set entrySet() { 417 return UnmodifiableSet.decorate(new AbstractSet () { 418 public Iterator iterator() { 419 return entryIterator(); 420 } 421 public int size() { 422 return BeanMap.this.readMethods.size(); 423 } 424 }); 425 } 426 427 433 public Collection values() { 434 ArrayList answer = new ArrayList ( readMethods.size() ); 435 for ( Iterator iter = valueIterator(); iter.hasNext(); ) { 436 answer.add( iter.next() ); 437 } 438 return UnmodifiableList.decorate(answer); 439 } 440 441 442 445 452 public Class getType(String name) { 453 return (Class ) types.get( name ); 454 } 455 456 463 public Iterator keyIterator() { 464 return readMethods.keySet().iterator(); 465 } 466 467 472 public Iterator valueIterator() { 473 final Iterator iter = keyIterator(); 474 return new Iterator () { 475 public boolean hasNext() { 476 return iter.hasNext(); 477 } 478 public Object next() { 479 Object key = iter.next(); 480 return get(key); 481 } 482 public void remove() { 483 throw new UnsupportedOperationException ( "remove() not supported for BeanMap" ); 484 } 485 }; 486 } 487 488 493 public Iterator entryIterator() { 494 final Iterator iter = keyIterator(); 495 return new Iterator () { 496 public boolean hasNext() { 497 return iter.hasNext(); 498 } 499 public Object next() { 500 Object key = iter.next(); 501 Object value = get(key); 502 return new MyMapEntry( BeanMap.this, key, value ); 503 } 504 public void remove() { 505 throw new UnsupportedOperationException ( "remove() not supported for BeanMap" ); 506 } 507 }; 508 } 509 510 511 514 520 public Object getBean() { 521 return bean; 522 } 523 524 530 public void setBean( Object newBean ) { 531 bean = newBean; 532 reinitialise(); 533 } 534 535 541 public Method getReadMethod(String name) { 542 return (Method ) readMethods.get(name); 543 } 544 545 551 public Method getWriteMethod(String name) { 552 return (Method ) writeMethods.get(name); 553 } 554 555 556 559 567 protected Method getReadMethod( Object name ) { 568 return (Method ) readMethods.get( name ); 569 } 570 571 579 protected Method getWriteMethod( Object name ) { 580 return (Method ) writeMethods.get( name ); 581 } 582 583 587 protected void reinitialise() { 588 readMethods.clear(); 589 writeMethods.clear(); 590 types.clear(); 591 initialise(); 592 } 593 594 private void initialise() { 595 if(getBean() == null) return; 596 597 Class beanClass = getBean().getClass(); 598 try { 599 BeanInfo beanInfo = Introspector.getBeanInfo( beanClass ); 601 PropertyDescriptor [] propertyDescriptors = beanInfo.getPropertyDescriptors(); 602 if ( propertyDescriptors != null ) { 603 for ( int i = 0; i < propertyDescriptors.length; i++ ) { 604 PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; 605 if ( propertyDescriptor != null ) { 606 String name = propertyDescriptor.getName(); 607 Method readMethod = propertyDescriptor.getReadMethod(); 608 Method writeMethod = propertyDescriptor.getWriteMethod(); 609 Class aType = propertyDescriptor.getPropertyType(); 610 611 if ( readMethod != null ) { 612 readMethods.put( name, readMethod ); 613 } 614 if ( writeMethods != null ) { 615 writeMethods.put( name, writeMethod ); 616 } 617 types.put( name, aType ); 618 } 619 } 620 } 621 } 622 catch ( IntrospectionException e ) { 623 logWarn( e ); 624 } 625 } 626 627 636 protected void firePropertyChange( Object key, Object oldValue, Object newValue ) { 637 } 638 639 642 645 protected static class MyMapEntry extends AbstractMapEntry { 646 private BeanMap owner; 647 648 655 protected MyMapEntry( BeanMap owner, Object key, Object value ) { 656 super( key, value ); 657 this.owner = owner; 658 } 659 660 666 public Object setValue(Object value) { 667 Object key = getKey(); 668 Object oldValue = owner.get( key ); 669 670 owner.put( key, value ); 671 Object newValue = owner.get( key ); 672 super.setValue( newValue ); 673 return oldValue; 674 } 675 } 676 677 691 protected Object [] createWriteMethodArguments( Method method, Object value ) throws IllegalAccessException , ClassCastException { 692 try { 693 if ( value != null ) { 694 Class [] types = method.getParameterTypes(); 695 if ( types != null && types.length > 0 ) { 696 Class paramType = types[0]; 697 if ( ! paramType.isAssignableFrom( value.getClass() ) ) { 698 value = convertType( paramType, value ); 699 } 700 } 701 } 702 Object [] answer = { value }; 703 return answer; 704 } 705 catch ( InvocationTargetException e ) { 706 logInfo( e ); 707 throw new IllegalArgumentException ( e.getMessage() ); 708 } 709 catch ( InstantiationException e ) { 710 logInfo( e ); 711 throw new IllegalArgumentException ( e.getMessage() ); 712 } 713 } 714 715 746 protected Object convertType( Class newType, Object value ) 747 throws InstantiationException , IllegalAccessException , IllegalArgumentException , InvocationTargetException { 748 749 Class [] types = { value.getClass() }; 751 try { 752 Constructor constructor = newType.getConstructor( types ); 753 Object [] arguments = { value }; 754 return constructor.newInstance( arguments ); 755 } 756 catch ( NoSuchMethodException e ) { 757 Transformer transformer = getTypeTransformer( newType ); 759 if ( transformer != null ) { 760 return transformer.transform( value ); 761 } 762 return value; 763 } 764 } 765 766 773 protected Transformer getTypeTransformer( Class aType ) { 774 return (Transformer) defaultTransformers.get( aType ); 775 } 776 777 783 protected void logInfo(Exception ex) { 784 System.out.println( "INFO: Exception: " + ex ); 786 } 787 788 794 protected void logWarn(Exception ex) { 795 System.out.println( "WARN: Exception: " + ex ); 797 ex.printStackTrace(); 798 } 799 } 800 | Popular Tags |