1 19 20 package org.netbeans.modules.java; 21 22 import java.awt.Image ; 23 import java.beans.PropertyChangeEvent ; 24 import java.beans.PropertyChangeListener ; 25 import java.io.File ; 26 import java.io.IOException ; 27 import java.io.ObjectInputStream ; 28 import java.lang.ref.WeakReference ; 29 import java.lang.reflect.InvocationTargetException ; 30 import java.net.URI ; 31 import java.net.URL ; 32 import java.nio.charset.Charset ; 33 import java.text.MessageFormat ; 34 import java.util.ArrayList ; 35 import java.util.Arrays ; 36 import java.util.HashSet ; 37 import java.util.Iterator ; 38 import java.util.List ; 39 import java.util.Set ; 40 import javax.jmi.reflect.InvalidObjectException; 41 import javax.jmi.reflect.JmiException; 42 import javax.swing.SwingUtilities ; 43 import javax.swing.event.ChangeEvent ; 44 import javax.swing.event.ChangeListener ; 45 import org.netbeans.api.java.classpath.ClassPath; 46 import org.netbeans.api.mdr.MDRepository; 47 import org.netbeans.api.mdr.events.ExtentEvent; 48 import org.netbeans.api.mdr.events.MDRChangeEvent; 49 import org.netbeans.api.mdr.events.MDRChangeListener; 50 import org.netbeans.api.mdr.events.MDRChangeSource; 51 import org.netbeans.api.queries.FileBuiltQuery; 52 import org.netbeans.jmi.javamodel.Resource; 53 import org.netbeans.modules.java.settings.JavaSettings; 54 import org.netbeans.modules.java.tools.BadgeCache; 55 import org.netbeans.modules.java.ui.nodes.SourceNodes; 56 import org.netbeans.modules.java.ui.nodes.elements.SourceChildren; 57 import org.netbeans.modules.java.ui.nodes.elements.SourceEditSupport; 58 import org.netbeans.modules.javacore.api.JavaModel; 59 import org.netbeans.modules.javacore.internalapi.JavaMetamodel; 60 import org.netbeans.modules.javacore.internalapi.ParsingListener; 61 import org.openide.ErrorManager; 62 import org.openide.filesystems.FileObject; 63 import org.openide.filesystems.FileStateInvalidException; 64 import org.openide.filesystems.FileUtil; 65 import org.openide.loaders.DataNode; 66 import org.openide.loaders.DataObject; 67 import org.openide.nodes.Children; 68 import org.openide.nodes.Node; 69 import org.openide.nodes.PropertySupport; 70 import org.openide.nodes.PropertySupport.ReadOnly; 71 import org.openide.nodes.Sheet; 72 import org.openide.util.Lookup; 73 import org.openide.util.NbBundle; 74 import org.openide.util.RequestProcessor; 75 import org.openide.util.Utilities; 76 import org.openide.util.WeakListeners; 77 import org.openide.util.datatransfer.NewType; 78 79 82 public class JavaNode extends DataNode { 83 84 private static final boolean DONT_RESOLVE_JAVA_BADGES = Boolean.getBoolean("perf.dont.resolve.java.badges"); 85 86 private static final String SHEETNAME_TEXT_PROPERTIES = "textProperties"; private static final String PROP_ENCODING = "encoding"; 89 90 private static final long serialVersionUID = -7396485743899766258L; 91 92 private static final String ICON_BASE = "org/netbeans/modules/java/resources/"; 95 private static final String BADGE_MAIN = ICON_BASE + "executable-badge"; private static final String BADGE_ERROR = ICON_BASE + "error-badge"; private static final String BADGE_NEEDS_COMPILE = ICON_BASE + "needs-compile"; 99 private static final String [] ICON_ATTRS_NONE = {}; 100 101 protected static final String ICON_ATTR_NEEDS_COMPILE = "needsCompile"; 105 protected static final String ICON_ATTR_MAIN = "mainClass"; 107 110 protected static final String ICON_ATTR_ERROR = "error"; 112 private static final int ICON_REFRESH_DELAY_MSECS = 1000; 113 114 private BadgeCache iconCache; 115 116 private HashSet currentBadges; 117 118 private StatePropagator statePropagator; 119 120 123 public JavaNode (JavaDataObject jdo) { 124 this(jdo, jdo.isTemplate() ? Children.LEAF : new JavaSourceChildren(jdo)); 125 currentBadges = new HashSet (); 126 } 127 128 private static final class JavaSourceChildren extends SourceChildren { 130 JavaDataObject jdo; 131 private ExtentListener wExtentL; 132 133 public JavaSourceChildren (JavaDataObject jdo) { 134 super(SourceNodes.getExplorerFactory()); 135 this.jdo = jdo; 136 } 137 138 protected void removeNotify() { 139 super.removeNotify(); 140 setElement (null); 141 } 142 143 protected Resource createResource() { 144 Resource r = null; 145 JavaModel.getJavaRepository().beginTrans(false); 146 try { 147 r = JavaModel.getResource(jdo.getPrimaryFile()); 148 } catch (JmiException je) { 149 } 152 finally { 153 JavaModel.getJavaRepository().endTrans(); 154 } 155 return r; 156 } 157 158 public void setElement(final Resource element) { 159 MDRepository repository = JavaModel.getJavaRepository(); 160 if (this.element == null && element != null) { 161 if (wExtentL == null) { 162 wExtentL = new ExtentListener(this, (MDRChangeSource) repository); 163 } 164 ((MDRChangeSource) repository).addListener(wExtentL); 165 } else if (element == null) { 166 ((MDRChangeSource) repository).removeListener(wExtentL); 167 } 168 super.setElement(element); 169 } 170 171 173 174 private static final class ExtentListener extends WeakReference implements MDRChangeListener, Runnable { 175 176 private final MDRChangeSource source; 177 178 public ExtentListener(JavaSourceChildren referent, MDRChangeSource source) { 179 super(referent, Utilities.activeReferenceQueue()); 180 this.source = source; 181 } 182 183 public void change(MDRChangeEvent e) { 184 JavaSourceChildren sc = (JavaSourceChildren) get(); 185 if (sc == null) return; 186 if ((e instanceof ExtentEvent) && ((ExtentEvent) e).getType() == ExtentEvent.EVENT_EXTENT_DELETE) { 188 sc.setElement(JavaModel.getResource(sc.jdo.getPrimaryFile())); 189 } 190 } 191 192 public void run() { 193 source.removeListener(this); 194 } 195 196 } 197 198 } 200 206 protected void initializeBadges(BadgeCache cache) { 207 cache.registerBadge(ICON_ATTR_MAIN, BADGE_MAIN, 9, 8); 208 cache.registerBadge(ICON_ATTR_ERROR, BADGE_ERROR, 8, 8); 209 cache.registerBadge(ICON_ATTR_NEEDS_COMPILE, BADGE_NEEDS_COMPILE, 16, 0); 210 } 211 212 220 public JavaNode (JavaDataObject jdo, Children children) { 221 super (jdo, children); 222 initialize(); 223 } 224 225 public void setIconBase(String base) { 226 super.setIconBase(base); 227 synchronized (this) { 228 iconCache = BadgeCache.getCache(base); 229 initializeBadges(iconCache); 230 } 231 } 232 233 private void initialize () { 234 setIconBase(getBareIconBase()); 235 StateUpdater.registerNode(this); 236 } 237 238 243 protected void requestResolveIcons() { 244 StateUpdater.registerNode(this); 245 } 246 247 private void readObject(ObjectInputStream is) throws IOException , ClassNotFoundException { 248 is.defaultReadObject(); 249 initialize(); 250 } 251 252 static final void wrapThrowable(Throwable outer, Throwable inner, String message) { 253 ErrorManager.getDefault().annotate( 254 outer, ErrorManager.USER, null, message, inner, null); 255 } 256 257 261 protected Sheet createSheet () { 262 Sheet sheet = super.createSheet(); 263 264 if (getRenameHandler() != null) 267 sheet.get(Sheet.PROPERTIES).put(createNameProperty()); 268 269 Sheet.Set ps = new Sheet.Set(); 270 ps.setName(SHEETNAME_TEXT_PROPERTIES); 271 ps.setDisplayName(Util.getString("PROP_textfileSetName")); ps.setShortDescription(Util.getString("HINT_textfileSetName")); ps.put(new PropertySupport.ReadWrite(PROP_ENCODING, 274 String .class, Util.getString("PROP_fileEncoding"), Util.getString("HINT_fileEncoding")) { public Object getValue() { 276 String enc = Util.getFileEncoding0(getDataObject().getPrimaryFile()); 277 if (enc == null) 278 return ""; 279 else 280 return enc; 281 } 282 283 public void setValue(Object enc) throws InvocationTargetException { 284 String encoding = (String )enc; 285 if (encoding != null) { 286 if (!"".equals(encoding)) { 287 try { 288 Charset.forName(encoding); 289 } catch (IllegalArgumentException ex) { 290 InvocationTargetException t = new InvocationTargetException (ex); 292 wrapThrowable(t, ex, 293 MessageFormat.format(Util.getString("FMT_UnsupportedEncoding"), new Object [] { 295 encoding 296 } 297 )); 298 throw t; 299 } 300 } else 301 encoding = null; 302 } 303 try { 304 Util.setFileEncoding(getDataObject().getPrimaryFile(), encoding); 305 ((JavaDataObject)getDataObject()).firePropertyChange0(PROP_ENCODING, null, null); 306 } catch (IOException ex) { 307 throw new InvocationTargetException (ex); 308 } 309 } 310 }); 311 sheet.put(ps); 312 ps = new Sheet.Set(); 314 ps.setName("classpaths"); ps.setDisplayName(NbBundle.getMessage(JavaNode.class, "LBL_JavaNode_sheet_classpaths")); 316 ps.setShortDescription(NbBundle.getMessage(JavaNode.class, "HINT_JavaNode_sheet_classpaths")); 317 ps.put(new Node.Property[] { 318 new ClasspathProperty(ClassPath.COMPILE, 319 NbBundle.getMessage(JavaNode.class, "PROP_JavaNode_compile_classpath"), 320 NbBundle.getMessage(JavaNode.class, "HINT_JavaNode_compile_classpath")), 321 new ClasspathProperty(ClassPath.EXECUTE, 322 NbBundle.getMessage(JavaNode.class, "PROP_JavaNode_execute_classpath"), 323 NbBundle.getMessage(JavaNode.class, "HINT_JavaNode_execute_classpath")), 324 new ClasspathProperty(ClassPath.BOOT, 325 NbBundle.getMessage(JavaNode.class, "PROP_JavaNode_boot_classpath"), 326 NbBundle.getMessage(JavaNode.class, "HINT_JavaNode_boot_classpath")), 327 }); 328 sheet.put(ps); 329 return sheet; 330 } 331 332 private Node.Property createNameProperty () { 333 Node.Property p = new PropertySupport.ReadWrite ( 334 DataObject.PROP_NAME, 335 String .class, 336 NbBundle.getMessage (DataObject.class, "PROP_name"), 337 NbBundle.getMessage (DataObject.class, "HINT_name") 338 ) { 339 public Object getValue () { 340 return JavaNode.this.getName(); 341 } 342 343 public Object getValue(String key) { 344 if ("suppressCustomEditor".equals (key)) { return Boolean.TRUE; 346 } else { 347 return super.getValue (key); 348 } 349 } 350 public void setValue(Object val) throws IllegalAccessException , 351 IllegalArgumentException , InvocationTargetException { 352 if (!canWrite()) 353 throw new IllegalAccessException (); 354 if (!(val instanceof String )) 355 throw new IllegalArgumentException (); 356 357 JavaNode.this.setName((String )val); 358 } 359 360 public boolean canWrite() { 361 return JavaNode.this.canRename(); 362 } 363 364 }; 365 366 return p; 367 } 368 369 374 private final class ClasspathProperty extends PropertySupport.ReadOnly { 375 376 private final String id; 377 378 public ClasspathProperty(String id, String displayName, String shortDescription) { 379 super(id, String .class, displayName, shortDescription); 380 this.id = id; 381 setValue("oneline", Boolean.FALSE); } 384 385 public Object getValue() { 386 ClassPath cp = ClassPath.getClassPath(getDataObject().getPrimaryFile(), id); 387 if (cp != null) { 388 StringBuffer sb = new StringBuffer (); 389 Iterator entries = cp.entries().iterator(); 390 while (entries.hasNext()) { 391 ClassPath.Entry entry = (ClassPath.Entry) entries.next(); 392 URL u = entry.getURL(); 393 append(sb, u); 394 } 395 return sb.toString(); 396 } else { 397 return NbBundle.getMessage(JavaNode.class, "LBL_JavaNode_classpath_unknown"); 398 } 399 } 400 401 private void append(StringBuffer sb, URL u) { 402 String item = u.toExternalForm(); if (u.getProtocol().equals("file")) { item = new File (URI.create(item)).getAbsolutePath(); 405 } else if (u.getProtocol().equals("jar") && item.endsWith("!/")) { URL embedded = FileUtil.getArchiveFile(u); 407 assert embedded != null : u; 408 if (embedded.getProtocol().equals("file")) { item = new File (URI.create(embedded.toExternalForm())).getAbsolutePath(); 410 } 411 } 412 if (sb.length() > 0) { 413 sb.append(File.pathSeparatorChar); 414 } 415 sb.append(item); 416 } 417 } 418 419 423 protected JavaDataObject getJavaDataObject() { 424 return (JavaDataObject) getDataObject(); 425 } 426 427 public Image getIcon(int type) { 428 return iconCache.getIcon(super.getIcon(type), getBadges()); 429 } 430 431 public Image getOpenedIcon(int type) { 432 return iconCache.getIcon(super.getOpenedIcon(type), getBadges()); 433 } 434 435 public void setName(String name) { 436 RenameHandler handler = getRenameHandler(); 437 if (handler == null) { 438 super.setName(name); 439 } else { 440 try { 441 handler.handleRename(JavaNode.this, name); 442 } catch (IllegalArgumentException ioe) { 443 super.setName(name); 444 } 445 } 446 } 447 448 private static synchronized RenameHandler getRenameHandler() { 449 Lookup.Result renameImplementations = Lookup.getDefault().lookup(new Lookup.Template(RenameHandler.class)); 450 List handlers = (List ) renameImplementations.allInstances(); 451 if (handlers.size()==0) 452 return null; 453 if (handlers.size()>1) 454 ErrorManager.getDefault().log(ErrorManager.WARNING, "Multiple instances of RenameHandler found in Lookup; only using first one: " + handlers); return (RenameHandler) handlers.get(0); 456 } 457 458 467 protected String getIconBase() { 468 return ICON_BASE; 469 } 470 471 478 protected String getBareIconBase() { 479 return getIconBase() + getIcons()[0]; 480 } 481 482 498 protected String [] getIcons() { 499 return new String [] { "class" }; } 501 502 506 protected String [] getBadges() { 507 if (currentBadges.isEmpty()) 508 return null; 509 return (String [])currentBadges.toArray(new String [currentBadges.size()]); 510 } 511 512 518 protected void setBadges(String [] badges) { 519 if (badges == null) 520 badges = new String [0]; 521 if (currentBadges.size() == badges.length) { 522 boolean match = true; 523 524 for (int i = 0; i < badges.length; i++) { 525 if (!currentBadges.contains(badges[i])) { 526 match = false; 527 break; 528 } 529 } 530 if (match) 531 return; 532 } 533 currentBadges = new HashSet (Arrays.asList(badges)); 534 fireIconChange(); 535 fireOpenedIconChange(); 536 } 537 538 544 protected void resolveIcons() { 545 if (DONT_RESOLVE_JAVA_BADGES) 546 return ; 547 if (this.statePropagator == null) { 548 this.statePropagator = StatePropagator.getDefault(this); 549 } 550 JavaDataObject jdo = getJavaDataObject(); 551 FileObject fo = jdo.getPrimaryFile(); 552 boolean isTemplate; 553 try { 554 isTemplate = fo.getFileSystem().isDefault() && jdo.isTemplate(); 555 } catch (FileStateInvalidException fse) { 556 isTemplate = false; 557 } 558 559 if (isTemplate) 561 return; 562 if (fo.isVirtual()) 564 return; 565 566 final java.util.Collection badges = new java.util.ArrayList (3); 567 final String desc; 568 569 String pack2 = ""; boolean isValidResource = false; 571 JavaModel.getJavaRepository().beginTrans(false); 572 try { 573 JavaModel.setClassPath(fo); 574 Resource resource = JavaModel.getResource(fo); 575 isValidResource = resource != null && resource.isValid(); 576 if (isValidResource) { 577 pack2 = resource.getPackageName(); 578 } 579 } finally { 580 JavaModel.getJavaRepository().endTrans(); 581 } 582 if (isValidResource) { 583 587 FileObject parent = fo.getParent(); 588 ClassPath cp = ClassPath.getClassPath(parent, ClassPath.SOURCE); 589 String pack = (cp == null) ? null : cp.getResourceName(parent, '.',false); if (pack == null || !pack.equals(pack2)) { 592 desc = new MessageFormat (Util.getString("FMT_Bad_Package")).format(new Object [] { pack2 }); 593 } else { 595 desc = null; 596 } 597 598 if (hasMain()) { 599 badges.add(ICON_ATTR_MAIN); 600 } 601 } else { 602 if (currentBadges.contains(ICON_ATTR_MAIN)) { 603 badges.add(ICON_ATTR_MAIN); 604 } 605 desc=getShortDescription(); 606 } 607 608 609 if (!isTemplate) { 612 String compBadge = resolveCompileBadge(); 613 if (compBadge != null) 614 badges.add(compBadge); 615 } 616 617 SwingUtilities.invokeLater(new Runnable () { 618 public void run() { 619 setShortDescription(desc); 620 if (badges.isEmpty()) { 621 setBadges(ICON_ATTRS_NONE); 622 } else { 623 setBadges((String [])badges.toArray(new String [badges.size()])); 624 } 625 } 626 }); 627 } 628 629 private boolean hasMain() { 630 FileObject fo = getJavaDataObject().getPrimaryFile(); 631 if(!fo.isValid()) 632 return false; 633 JavaModel.getJavaRepository().beginTrans(false); 634 try { 635 JavaModel.setClassPath(fo); 636 Resource r = JavaModel.getResource(fo); 637 638 return r!=null && !r.getMain().isEmpty(); 639 } finally { 640 JavaModel.getJavaRepository().endTrans(); 641 } 642 } 643 644 protected String resolveCompileBadge() { 645 if (!JavaSettings.getDefault().isCompileStatusEnabled()) { 646 return null; 647 } 648 FileBuiltQuery.Status upToDate = FileBuiltQuery.getStatus(getDataObject().getPrimaryFile()); 649 return (upToDate != null && !upToDate.isBuilt()) ? ICON_ATTR_NEEDS_COMPILE : null; 650 651 } 652 653 public NewType[] getNewTypes() { 654 if (getJavaDataObject().isJavaFileReadOnly()) { 655 return super.getNewTypes(); 656 } 657 658 return SourceEditSupport.createJavaNodeNewTypes(this); 659 } 660 661 private static final class StateUpdater implements Runnable { 662 663 private static StateUpdater INSTANCE; 664 665 private Set registeredNodes; 666 private final RequestProcessor QUEUE; 667 private RequestProcessor.Task task; 668 669 670 private StateUpdater() { 671 registeredNodes = new HashSet (37); 672 QUEUE = new RequestProcessor("Java Node State Updater"); } 674 675 680 public static void registerNode(JavaNode node) { 681 init(); 682 INSTANCE.scheduleNode(node); 683 } 684 685 private static void init() { 686 if (INSTANCE == null) { 687 INSTANCE = new StateUpdater(); 688 } 689 } 690 691 private synchronized void scheduleNode(JavaNode node) { 692 if (!registeredNodes.contains(node)) { 693 registeredNodes.add(node); 694 if (task == null) { 695 task = QUEUE.post(this, JavaNode.ICON_REFRESH_DELAY_MSECS); 696 } else { 697 task.schedule(JavaNode.ICON_REFRESH_DELAY_MSECS); 698 } 699 } 700 } 701 702 public void run() { 703 List nodes; 704 synchronized(this) { 705 nodes = new ArrayList (this.registeredNodes); 706 this.registeredNodes.clear(); 707 } 708 updateNodes(nodes); 709 } 710 711 private void updateNodes(List nodes) { 712 for (Iterator it = nodes.iterator(); it.hasNext();) { 713 JavaNode node = (JavaNode) it.next(); 714 if (node != null) { 715 node.resolveIcons(); 716 } 717 } 718 } 719 720 } 721 722 727 private static final class StatePropagator 728 extends WeakReference 729 implements PropertyChangeListener , ChangeListener , ParsingListener, Runnable { 730 731 private FileBuiltQuery.Status upToDate; 732 private final DataObject dobj; 733 private final PropertyChangeListener weakPropLsnr; 734 private final ParsingListener weakParsingLsnr; 735 736 private StatePropagator(JavaNode node) { 737 super(node, Utilities.activeReferenceQueue()); 738 this.dobj = node.getDataObject(); 739 740 dobj.addPropertyChangeListener(this); 741 registerBuildStatusListener(); 742 743 this.weakPropLsnr = WeakListeners.propertyChange(this, JavaSettings.getDefault()); 745 JavaSettings.getDefault().addPropertyChangeListener(weakPropLsnr); 746 this.weakParsingLsnr = new WeakParsingListener(this); 747 JavaMetamodel.addParsingListener(weakParsingLsnr); 748 } 749 750 755 public static StatePropagator getDefault(JavaNode node) { 756 return new StatePropagator(node); 757 } 758 759 public void propertyChange(PropertyChangeEvent evt) { 760 Object src = evt.getSource(); 761 String name = evt.getPropertyName(); 762 JavaNode node = getJavaNode(); 763 if ((JavaSettings.PROP_SHOW_COMPILE_STATUS.equals(name) || name == null) && src == JavaSettings.getDefault()) { 764 registerBuildStatusListener(); 765 StateUpdater.registerNode(node); 766 } else if ((DataObject.PROP_PRIMARY_FILE.equals(name) || name == null) && src == node.getDataObject()) { 767 registerBuildStatusListener(); 769 StateUpdater.registerNode(node); 770 } 771 } 772 773 public void stateChanged(ChangeEvent e) { 774 JavaNode node = getJavaNode(); 775 if (node != null && JavaSettings.getDefault().isCompileStatusEnabled()) { 776 StateUpdater.registerNode(node); 777 } 778 } 779 780 public void resourceParsed(Resource rsc) { 781 JavaNode node = getJavaNode(); 782 if (node != null) { 783 FileObject fo = node.getDataObject().getPrimaryFile(); 784 try { 785 if (fo.getPath().endsWith(rsc.getName()) && fo.equals(JavaModel.getFileObject(rsc))) { 786 StateUpdater.registerNode(node); 787 } 788 } catch (InvalidObjectException ioe) { 789 } 792 } 793 } 794 795 public void run() { 796 JavaSettings.getDefault().removePropertyChangeListener(this.weakPropLsnr); 798 this.dobj.removePropertyChangeListener(this); 799 if (this.upToDate != null) { 800 this.upToDate.removeChangeListener(this); 801 } 802 } 803 804 private JavaNode getJavaNode() { 805 return (JavaNode) get(); 806 } 807 808 private void registerBuildStatusListener() { 809 JavaNode node = getJavaNode(); 810 if (node != null) { 811 FileBuiltQuery.Status status = this.upToDate; 812 if (status != null) { 813 status.removeChangeListener(this); 814 } 815 if (JavaSettings.getDefault().isCompileStatusEnabled()) { 816 status = FileBuiltQuery.getStatus(node.getDataObject().getPrimaryFile()); 817 if (status != null) { 818 status.addChangeListener(this); 819 } 820 } else { 821 status = null; 822 } 823 this.upToDate = status; 824 } 825 } 826 } 827 828 private static final class WeakParsingListener extends WeakReference 829 implements Runnable , ParsingListener { 830 831 public WeakParsingListener(ParsingListener delegate) { 832 super(delegate, Utilities.activeReferenceQueue()); 833 } 834 835 public void run() { 836 JavaMetamodel.removeParsingListener(this); 837 } 838 839 public void resourceParsed(Resource rsc) { 840 ParsingListener delegate = (ParsingListener) get(); 841 if (delegate != null) { 842 delegate.resourceParsed(rsc); 843 } 844 } 845 } 846 } 847 | Popular Tags |