1 19 20 package org.openide.nodes; 21 22 import java.awt.Image ; 23 import java.beans.BeanDescriptor ; 24 import java.beans.BeanInfo ; 25 import java.beans.Beans ; 26 import java.beans.Customizer ; 27 import java.beans.EventSetDescriptor ; 28 import java.beans.IndexedPropertyDescriptor ; 29 import java.beans.IntrospectionException ; 30 import java.beans.Introspector ; 31 import java.beans.PropertyChangeEvent ; 32 import java.beans.PropertyChangeListener ; 33 import java.beans.PropertyDescriptor ; 34 import java.beans.beancontext.BeanContext ; 35 import java.beans.beancontext.BeanContextChild ; 36 import java.beans.beancontext.BeanContextProxy ; 37 import java.io.IOException ; 38 import java.lang.reflect.Method ; 39 import java.lang.reflect.Modifier ; 40 import java.util.ArrayList ; 41 import java.util.Enumeration ; 42 import java.util.logging.Level ; 43 import java.util.logging.Logger ; 44 import javax.swing.Action ; 45 import org.openide.util.Exceptions; 46 import org.openide.util.HelpCtx; 47 import org.openide.util.Utilities; 48 import org.openide.util.Lookup; 49 import org.openide.util.WeakListeners; 50 import org.openide.util.actions.SystemAction; 51 52 61 public class BeanNode<T> extends AbstractNode { 62 64 65 private static final String ICON_BASE = "org/openide/nodes/beans.gif"; 67 69 70 private final T bean; 71 72 73 private BeanInfo beanInfo; 74 75 76 private Method nameGetter = null; 77 private Method nameSetter = null; 78 79 80 private Method removePCLMethod = null; 81 82 83 private PropL propertyChangeListener = null; 84 85 86 private boolean synchronizeName; 87 88 90 97 public BeanNode(T bean) throws IntrospectionException { 98 this(bean, null, null); 99 } 100 101 107 protected BeanNode(T bean, Children children) 108 throws IntrospectionException { 109 this(bean, children, null); 110 } 111 112 129 protected BeanNode(T bean, Children children, Lookup lkp) 130 throws IntrospectionException { 131 super((children == null) ? getChildren(bean) : children, lkp); 132 133 if (bean == null) { 134 throw new NullPointerException ("cannot make a node for a null bean"); } 136 137 this.bean = bean; 138 139 try { 140 initialization(lkp != null); 141 } catch (IntrospectionException ie) { 142 throw ie; 143 } catch (RuntimeException re) { 144 throw mkie(re); 145 } catch (LinkageError le) { 146 throw mkie(le); 147 } 148 } 149 150 private static Children getChildren(Object bean) { 151 if (bean instanceof BeanContext ) { 152 return new BeanChildren((BeanContext ) bean); 153 } 154 155 if (bean instanceof BeanContextProxy ) { 156 BeanContextChild bch = ((BeanContextProxy ) bean).getBeanContextProxy(); 157 158 if (bch instanceof BeanContext ) { 159 return new BeanChildren((BeanContext ) bch); 160 } 161 } 162 163 return Children.LEAF; 164 } 165 166 private static IntrospectionException mkie(Throwable t) { 167 return (IntrospectionException ) new IntrospectionException (t.toString()).initCause(t); 168 } 169 170 185 protected void setSynchronizeName(boolean watch) { 186 synchronizeName = watch; 187 } 188 189 192 protected T getBean() { 193 return bean; 194 } 195 196 199 public void destroy() throws IOException { 200 if (removePCLMethod != null) { 201 try { 202 Object o = Beans.getInstanceOf(bean, removePCLMethod.getDeclaringClass()); 203 removePCLMethod.invoke(o, new Object [] { propertyChangeListener }); 204 } catch (Exception e) { 205 NodeOp.exception(e); 206 } 207 } 208 209 super.destroy(); 210 } 211 212 215 public boolean canDestroy() { 216 return true; 217 } 218 219 224 public void setName(String s) { 225 if (synchronizeName) { 226 Method m = nameSetter; 227 228 if (m != null) { 229 try { 230 m.invoke(bean, new Object [] { s }); 231 } catch (Exception e) { 232 NodeOp.exception(e); 233 } 234 } 235 } 236 237 super.setName(s); 238 } 239 240 244 public boolean canRename() { 245 return !synchronizeName || (nameSetter != null); 246 } 247 248 254 public Image getIcon(int type) { 255 Image image = beanInfo.getIcon(type); 256 257 if (image != null) { 258 return image; 259 } 260 261 return super.getIcon(type); 262 } 263 264 269 public Image getOpenedIcon(int type) { 270 return getIcon(type); 271 } 272 273 public HelpCtx getHelpCtx() { 274 HelpCtx h = HelpCtx.findHelp(bean); 275 276 if (h != HelpCtx.DEFAULT_HELP) { 277 return h; 278 } else { 279 return new HelpCtx(BeanNode.class); 280 } 281 } 282 283 291 protected void createProperties(T bean, BeanInfo info) { 292 Descriptor d = computeProperties(bean, beanInfo); 293 294 Sheet sets = getSheet(); 295 Sheet.Set pset = Sheet.createPropertiesSet(); 296 pset.put(d.property); 297 298 BeanDescriptor bd = info.getBeanDescriptor(); 299 300 if ((bd != null) && (bd.getValue("propertiesHelpID") != null)) { pset.setValue("helpID", bd.getValue("propertiesHelpID")); } 303 304 sets.put(pset); 305 306 if (d.expert.length != 0) { 307 Sheet.Set eset = Sheet.createExpertSet(); 308 eset.put(d.expert); 309 310 if ((bd != null) && (bd.getValue("expertHelpID") != null)) { eset.setValue("helpID", bd.getValue("expertHelpID")); } 313 314 sets.put(eset); 315 } 316 } 317 318 321 public boolean canCopy() { 322 return true; 323 } 324 325 328 public boolean canCut() { 329 return false; 330 } 331 332 public Action [] getActions(boolean context) { 333 return NodeOp.createFromNames( 334 new String [] { "Copy", null, "Tools", "Properties" } 336 ); 337 } 338 339 344 public boolean hasCustomizer() { 345 return beanInfo.getBeanDescriptor().getCustomizerClass() != null; 347 } 348 349 352 public java.awt.Component getCustomizer() { 353 Class clazz = beanInfo.getBeanDescriptor().getCustomizerClass(); 354 355 if (clazz == null) { 356 return null; 357 } 358 359 Object o; 360 361 try { 362 o = clazz.newInstance(); 363 } catch (InstantiationException e) { 364 NodeOp.exception(e); 365 366 return null; 367 } catch (IllegalAccessException e) { 368 NodeOp.exception(e); 369 370 return null; 371 } 372 373 if (!(o instanceof Customizer )) { 374 return null; 377 } 378 379 Customizer cust = ((Customizer ) o); 380 381 TMUtil.attachCustomizer(this, cust); 382 383 java.awt.Component comp = null; 385 386 if (o instanceof java.awt.Component ) { 387 comp = (java.awt.Component ) o; 388 } else { 389 comp = TMUtil.createDialog(o); 391 } 392 393 if (comp == null) { 394 return null; 396 } 397 398 cust.setObject(bean); 399 400 if (removePCLMethod == null) { 401 cust.addPropertyChangeListener( 402 new PropertyChangeListener () { 403 public void propertyChange(PropertyChangeEvent e) { 404 firePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 405 } 406 } 407 ); 408 } 409 410 return comp; 411 } 412 413 427 public static Descriptor computeProperties(Object bean, BeanInfo info) { 428 ArrayList <Node.Property> property = new ArrayList <Node.Property>(); 429 ArrayList <Node.Property> expert = new ArrayList <Node.Property>(); 430 ArrayList <Node.Property> hidden = new ArrayList <Node.Property>(); 431 432 PropertyDescriptor [] propertyDescriptor = info.getPropertyDescriptors(); 433 434 int k = propertyDescriptor.length; 435 436 for (int i = 0; i < k; i++) { 437 if (propertyDescriptor[i].getPropertyType() == null) { 438 continue; 439 } 440 441 Node.Property prop; 442 443 if (propertyDescriptor[i] instanceof IndexedPropertyDescriptor ) { 444 IndexedPropertyDescriptor p = (IndexedPropertyDescriptor ) propertyDescriptor[i]; 445 446 if ((p.getReadMethod() != null) && (!p.getReadMethod().getReturnType().isArray())) { 447 continue; 451 } 452 453 IndexedPropertySupport support = new IndexedPropertySupport( 454 bean, p.getPropertyType(), p.getIndexedPropertyType(), p.getReadMethod(), p.getWriteMethod(), 455 p.getIndexedReadMethod(), p.getIndexedWriteMethod() 456 ); 457 support.setName(p.getName()); 458 support.setDisplayName(p.getDisplayName()); 459 support.setShortDescription(p.getShortDescription()); 460 461 for (Enumeration e = p.attributeNames(); e.hasMoreElements();) { 462 String aname = (String ) e.nextElement(); 463 support.setValue(aname, p.getValue(aname)); 464 } 465 466 prop = support; 467 } else { 468 PropertyDescriptor p = propertyDescriptor[i]; 469 470 PropertySupport.Reflection support = new PropertySupport.Reflection( 473 bean, p.getPropertyType(), p.getReadMethod(), p.getWriteMethod() 474 ); 475 support.setName(p.getName()); 476 support.setDisplayName(p.getDisplayName()); 477 support.setShortDescription(p.getShortDescription()); 478 support.setPropertyEditorClass(p.getPropertyEditorClass()); 479 480 for (Enumeration e = p.attributeNames(); e.hasMoreElements();) { 481 String aname = (String ) e.nextElement(); 482 support.setValue(aname, p.getValue(aname)); 483 } 484 485 prop = support; 486 } 487 488 Object help = propertyDescriptor[i].getValue("helpID"); 491 if ((help != null) && (help instanceof String )) { 492 prop.setValue("helpID", help); } 494 495 if (propertyDescriptor[i].isHidden()) { 497 hidden.add(prop); 499 } else { 500 if (propertyDescriptor[i].isExpert()) { 501 expert.add(prop); 502 prop.setExpert(true); 503 } else { 504 property.add(prop); 505 } 506 } 507 } 508 510 return new Descriptor (property, expert, hidden); 511 } 512 513 519 521 private void initialization(boolean hasLookup) throws IntrospectionException { 522 setIconBaseWithExtension(ICON_BASE); 523 524 setSynchronizeName(true); 525 526 Class clazz = bean.getClass(); 531 532 while (!Modifier.isPublic(clazz.getModifiers()) && !hasExplicitBeanInfo(clazz)) { 533 clazz = clazz.getSuperclass(); 534 535 if (clazz == null) { 536 clazz = Object .class; } 538 } 539 540 beanInfo = Utilities.getBeanInfo(clazz); 541 542 registerName(); 544 setNameSilently(getNameForBean()); 545 546 BeanDescriptor descriptor = beanInfo.getBeanDescriptor(); 547 String sd = descriptor.getShortDescription(); 548 549 if (!Utilities.compareObjects(sd, descriptor.getDisplayName())) { 550 setShortDescription(sd); 551 } 552 553 EventSetDescriptor [] eventSetDescriptors = beanInfo.getEventSetDescriptors(); 555 int i; 556 int k = eventSetDescriptors.length; 557 Method method = null; 558 559 for (i = 0; i < k; i++) { 560 method = eventSetDescriptors[i].getAddListenerMethod(); 561 562 if ( 563 (method != null) && method.getName().equals("addPropertyChangeListener") && Modifier.isPublic(method.getModifiers()) 565 ) { 566 break; 567 } 568 } 569 570 if (i != k) { 571 try { 572 Object o = Beans.getInstanceOf(bean, method.getDeclaringClass()); 573 propertyChangeListener = new PropL(); 574 method.invoke(o, new Object [] { WeakListeners.propertyChange(propertyChangeListener, o) }); 575 removePCLMethod = eventSetDescriptors[i].getRemoveListenerMethod(); 576 } catch (Exception e) { 577 Exceptions.attachMessage(e, 582 "Trying to invoke " + method + 583 " where introspected class is " + 584 clazz.getName()); NodeOp.warning(e); 586 } 587 } 588 589 createProperties(bean, beanInfo); 590 591 for (Enumeration e = beanInfo.getBeanDescriptor().attributeNames(); e.hasMoreElements();) { 592 String aname = (String ) e.nextElement(); 593 setValue(aname, beanInfo.getBeanDescriptor().getValue(aname)); 594 } 595 596 if (!hasLookup) { 597 Node.Cookie instanceCookie = TMUtil.createInstanceCookie(bean); 598 599 if (instanceCookie != null) { 600 getCookieSet().add(instanceCookie); 601 } 602 } 603 } 604 605 609 private boolean hasExplicitBeanInfo(Class <?> clazz) { 610 String className = clazz.getName(); 611 int indx = className.lastIndexOf('.'); 612 className = className.substring(indx + 1); 613 614 String [] paths = Introspector.getBeanInfoSearchPath(); 615 616 for (int i = 0; i < paths.length; i++) { 617 String s = paths[i] + '.' + className + "BeanInfo"; 619 try { 620 Class.forName(s); 622 623 return true; 624 } catch (ClassNotFoundException ex) { 625 } 627 } 628 629 return false; 630 } 631 632 634 638 private void registerName() { 639 Class <?> clazz = bean.getClass(); 641 642 while (!Modifier.isPublic(clazz.getModifiers())) { 644 clazz = clazz.getSuperclass(); 645 646 if (clazz == null) { 647 clazz = Object .class; 648 } 649 } 650 651 try { 653 try { 654 nameGetter = clazz.getMethod("getName"); 656 if (nameGetter.getReturnType() != String .class) { 657 throw new NoSuchMethodException (); 658 } 659 } catch (NoSuchMethodException e) { 660 try { 661 nameGetter = clazz.getMethod("getDisplayName"); 663 if (nameGetter.getReturnType() != String .class) { 664 throw new NoSuchMethodException (); 665 } 666 } catch (NoSuchMethodException ee) { 667 nameGetter = null; 668 669 return; 670 } 671 } 672 } catch (SecurityException se) { 673 NodeOp.exception(se); 674 nameGetter = null; 675 676 return; 677 } 678 679 try { 682 String result = (String ) nameGetter.invoke(bean); 684 } catch (Exception e) { 685 Exceptions.attachMessage(e, 686 "Bad method: " + clazz.getName() + "." + 687 nameGetter.getName()); 688 Logger.getLogger(BeanNode.class.getName()).log(Level.WARNING, null, e); 689 690 nameGetter = null; 691 692 return; 693 } 694 695 try { 697 try { 698 nameSetter = clazz.getMethod("setName", String .class); 701 if (nameSetter.getReturnType() != Void.TYPE) { 702 throw new NoSuchMethodException (); 703 } 704 } catch (NoSuchMethodException e) { 705 try { 706 nameSetter = clazz.getMethod("setDisplayName", String .class); 708 if (nameSetter.getReturnType() != Void.TYPE) { 709 throw new NoSuchMethodException (); 710 } 711 } catch (NoSuchMethodException ee) { 712 nameSetter = null; 713 } 714 } 715 } catch (SecurityException se) { 716 NodeOp.exception(se); 717 } 718 } 719 720 723 private String getNameForBean() { 724 if (nameGetter != null) { 725 try { 726 String name = (String ) nameGetter.invoke(bean); 727 728 return (name != null) ? name : ""; } catch (Exception ex) { 730 NodeOp.warning(ex); 731 } 732 } 733 734 BeanDescriptor descriptor = beanInfo.getBeanDescriptor(); 735 736 return descriptor.getDisplayName(); 737 } 738 739 741 void setNameSilently(String name) { 742 super.setName(name); 743 } 744 745 public Action getPreferredAction() { 746 SystemAction[] arr = NodeOp.createFromNames(new String [] { "Properties" }); 749 return (arr.length == 1) ? arr[0] : null; 750 } 751 752 755 public static final class Descriptor extends Object { 756 757 public final Node.Property[] property; 758 759 760 public final Node.Property[] expert; 761 762 763 public final Node.Property[] hidden; 764 765 766 Descriptor(ArrayList <Node.Property> p, ArrayList <Node.Property> e, ArrayList <Node.Property> h) { 767 property = new Node.Property[p.size()]; 768 p.toArray(property); 769 770 expert = new Node.Property[e.size()]; 771 e.toArray(expert); 772 773 hidden = new Node.Property[h.size()]; 774 h.toArray(hidden); 775 } 776 } 777 778 781 private final class PropL extends Object implements PropertyChangeListener { 782 PropL() { 783 } 784 785 public void propertyChange(PropertyChangeEvent e) { 786 String name = e.getPropertyName(); 787 788 if (name == null) { 789 firePropertyChange(null, e.getOldValue(), e.getNewValue()); 790 } else { 791 PropertyDescriptor [] arr = beanInfo.getPropertyDescriptors(); 792 793 for (int i = 0; i < arr.length; i++) { 794 if (arr[i].isHidden()) { 795 continue; 796 } 797 798 if (name.equals(arr[i].getName())) { 799 firePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 800 801 break; 802 } 803 } 804 } 805 806 if (synchronizeName) { 807 if ((name == null) || name.equals("name") || name.equals("displayName")) { 809 String newName = getNameForBean(); 810 811 if (!newName.equals(getName())) { 812 setNameSilently(newName); 813 } 814 } 815 } 816 } 817 } 818 } 819 | Popular Tags |