KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > JavaNode


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java;
21
22 import java.awt.Image JavaDoc;
23 import java.beans.PropertyChangeEvent JavaDoc;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.ObjectInputStream JavaDoc;
28 import java.lang.ref.WeakReference JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.net.URI JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.nio.charset.Charset JavaDoc;
33 import java.text.MessageFormat JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Arrays JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Set JavaDoc;
40 import javax.jmi.reflect.InvalidObjectException;
41 import javax.jmi.reflect.JmiException;
42 import javax.swing.SwingUtilities JavaDoc;
43 import javax.swing.event.ChangeEvent JavaDoc;
44 import javax.swing.event.ChangeListener JavaDoc;
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 /**
80  * The node representation of Java source files.
81  */

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 JavaDoc SHEETNAME_TEXT_PROPERTIES = "textProperties"; // NOI18N
87
private static final String JavaDoc PROP_ENCODING = "encoding"; // NOI18N
88

89     /** generated Serialized Version UID */
90     private static final long serialVersionUID = -7396485743899766258L;
91
92     private static final String JavaDoc ICON_BASE = "org/netbeans/modules/java/resources/"; // NOI18N
93
// private static final String BARE_ICON_BASE = "org/netbeans/modules/java/resources/class"; // NOI18N
94

95     private static final String JavaDoc BADGE_MAIN = ICON_BASE + "executable-badge"; // NOI18N
96
private static final String JavaDoc BADGE_ERROR = ICON_BASE + "error-badge"; // NOI18N
97
private static final String JavaDoc BADGE_NEEDS_COMPILE = ICON_BASE + "needs-compile"; // NOI18N
98

99     private static final String JavaDoc[] ICON_ATTRS_NONE = {};
100     
101     protected static final String JavaDoc ICON_ATTR_NEEDS_COMPILE = "needsCompile"; // NOI18N
102
/**
103      * Icon attribute -- main class.
104      */

105     protected static final String JavaDoc ICON_ATTR_MAIN = "mainClass"; // NOI18N
106

107     /**
108      * Icon attribute -- errors in the source.
109      */

110     protected static final String JavaDoc ICON_ATTR_ERROR = "error"; // NOI18N
111

112     private static final int ICON_REFRESH_DELAY_MSECS = 1000;
113
114     private BadgeCache iconCache;
115     
116     private HashSet JavaDoc currentBadges;
117
118     private StatePropagator statePropagator;
119     
120     /** Create a node for the Java data object using the default children.
121     * @param jdo the data object to represent
122     */

123     public JavaNode (JavaDataObject jdo) {
124     this(jdo, jdo.isTemplate() ? Children.LEAF : new JavaSourceChildren(jdo));
125         currentBadges = new HashSet JavaDoc();
126     }
127
128     // T.B.: Workaround for issue #28623 - aggresively creating source element, classloading
129
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                 //do nothing
150
//error node will be displayed
151
}
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         // innerclass ...........................................................
172

173         /** The listener for listening on extent unmount */
174         private static final class ExtentListener extends WeakReference JavaDoc implements MDRChangeListener, Runnable JavaDoc {
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 (sc.element == null || !sc.element.isValid()) return;
187
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     } // JavaSourceChildren ................
199

200     /**
201      * This method is called during node initialization and whenever the base icon is
202      * replaced to refill the badge cache with custom badges. The JavaNode's implementation
203      * adds executable and error badges. Override to add your own badges, or replace the
204      * default ones.
205      */

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     /** Create a node for the Java data object with configurable children.
213     * Subclasses should use this constructor if they wish to provide special display for the child nodes.
214     * Typically this would involve creating a subclass of {@link SourceChildren} based
215     * on the {@link JavaDataObject#getSource provided source element}; the children list
216     * may have extra nodes {@link Children#add(Node[]) added}, either at the {@link Children.Keys#setBefore beginning or end}.
217     * @param jdo the data object to represent
218     * @param children the children for this node
219     */

220     public JavaNode (JavaDataObject jdo, Children children) {
221         super (jdo, children);
222         initialize();
223     }
224     
225     public void setIconBase(String JavaDoc 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     /**
239      * Schedules a refresh of node's icon, after ICON_REFRESH_DELAY_MSEC.
240      * Requests is not scheduled if another one is still pending.
241      * @deprecated
242      */

243     protected void requestResolveIcons() {
244         StateUpdater.registerNode(this);
245     }
246
247     private void readObject(ObjectInputStream JavaDoc is) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
248         is.defaultReadObject();
249         initialize();
250     }
251     
252     static final void wrapThrowable(Throwable JavaDoc outer, Throwable JavaDoc inner, String JavaDoc message) {
253         ErrorManager.getDefault().annotate(
254             outer, ErrorManager.USER, null, message, inner, null);
255     }
256
257     /** Create the property sheet.
258     * Subclasses may want to override this and add additional properties.
259     * @return the sheet
260     */

261     protected Sheet createSheet () {
262         Sheet sheet = super.createSheet();
263         
264         //if there is any rename handler installed
265
//push under our own property
266
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")); // NOI18N
272
ps.setShortDescription(Util.getString("HINT_textfileSetName")); // NOI18N
273
ps.put(new PropertySupport.ReadWrite(PROP_ENCODING,
274             String JavaDoc.class, Util.getString("PROP_fileEncoding"), Util.getString("HINT_fileEncoding")) { // NOI18N
275
public Object JavaDoc getValue() {
276                 String JavaDoc enc = Util.getFileEncoding0(getDataObject().getPrimaryFile());
277                 if (enc == null)
278                     return "";
279                 else
280                     return enc;
281             }
282             
283             public void setValue(Object JavaDoc enc) throws InvocationTargetException JavaDoc {
284                 String JavaDoc encoding = (String JavaDoc)enc;
285                 if (encoding != null) {
286                     if (!"".equals(encoding)) {
287                         try {
288                             Charset.forName(encoding);
289                         } catch (IllegalArgumentException JavaDoc ex) {
290                             // IllegalCharsetNameException or UnsupportedCharsetException
291
InvocationTargetException JavaDoc t = new InvocationTargetException JavaDoc(ex);
292                             wrapThrowable(t, ex,
293                                 MessageFormat.format(Util.getString("FMT_UnsupportedEncoding"), // NOI18N
294
new Object JavaDoc[] {
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 JavaDoc ex) {
307                     throw new InvocationTargetException JavaDoc(ex);
308                 }
309             }
310         });
311         sheet.put(ps);
312         // Add classpath-related properties.
313
ps = new Sheet.Set();
314         ps.setName("classpaths"); // NOI18N
315
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 JavaDoc.class,
336                               NbBundle.getMessage (DataObject.class, "PROP_name"),
337                               NbBundle.getMessage (DataObject.class, "HINT_name")
338                           ) {
339                               public Object JavaDoc getValue () {
340                                   return JavaNode.this.getName();
341                               }
342
343                               public Object JavaDoc getValue(String JavaDoc key) {
344                                   if ("suppressCustomEditor".equals (key)) { //NOI18N
345
return Boolean.TRUE;
346                                   } else {
347                                       return super.getValue (key);
348                                   }
349                               }
350                               public void setValue(Object JavaDoc val) throws IllegalAccessException JavaDoc,
351                                       IllegalArgumentException JavaDoc, InvocationTargetException JavaDoc {
352                                   if (!canWrite())
353                                       throw new IllegalAccessException JavaDoc();
354                                   if (!(val instanceof String JavaDoc))
355                                       throw new IllegalArgumentException JavaDoc();
356                                   
357                                   JavaNode.this.setName((String JavaDoc)val);
358                               }
359                               
360                               public boolean canWrite() {
361                                   return JavaNode.this.canRename();
362                               }
363
364                           };
365
366         return p;
367     }
368     
369     /**
370      * Displays one kind of classpath for this Java source.
371      * Tries to use the normal format (directory or JAR names), falling back to URLs if necessary.
372      * Displays corresponding sources instead of binaries where this is possible.
373      */

374     private final class ClasspathProperty extends PropertySupport.ReadOnly {
375         
376         private final String JavaDoc id;
377         
378         public ClasspathProperty(String JavaDoc id, String JavaDoc displayName, String JavaDoc shortDescription) {
379             super(id, /*XXX NbClassPath would be preferable, but needs org.openide.execution*/String JavaDoc.class, displayName, shortDescription);
380             this.id = id;
381             // XXX the following does not always work... why?
382
setValue("oneline", Boolean.FALSE); // NOI18N
383
}
384
385         public Object JavaDoc getValue() {
386             ClassPath cp = ClassPath.getClassPath(getDataObject().getPrimaryFile(), id);
387             if (cp != null) {
388                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
389                 Iterator JavaDoc/*<ClassPath.Entry>*/ entries = cp.entries().iterator();
390                 while (entries.hasNext()) {
391                     ClassPath.Entry entry = (ClassPath.Entry) entries.next();
392                     URL JavaDoc 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 JavaDoc sb, URL JavaDoc u) {
402             String JavaDoc item = u.toExternalForm(); // fallback
403
if (u.getProtocol().equals("file")) { // NOI18N
404
item = new File JavaDoc(URI.create(item)).getAbsolutePath();
405             } else if (u.getProtocol().equals("jar") && item.endsWith("!/")) { // NOI18N
406
URL JavaDoc embedded = FileUtil.getArchiveFile(u);
407                 assert embedded != null : u;
408                 if (embedded.getProtocol().equals("file")) { // NOI18N
409
item = new File JavaDoc(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     /** Get the associated Java data object.
420     * ({@link #getDataObject} is protected; this provides access to it.)
421     * @return the data object
422     */

423     protected JavaDataObject getJavaDataObject() {
424         return (JavaDataObject) getDataObject();
425     }
426     
427     public Image JavaDoc getIcon(int type) {
428         return iconCache.getIcon(super.getIcon(type), getBadges());
429     }
430     
431     public Image JavaDoc getOpenedIcon(int type) {
432         return iconCache.getIcon(super.getOpenedIcon(type), getBadges());
433     }
434     
435     public void setName(String JavaDoc 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 JavaDoc 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 JavaDoc handlers = (List JavaDoc) 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); //NOI18N
455
return (RenameHandler) handlers.get(0);
456     }
457
458     /** Get the icon base.
459     * This should be a resource path, e.g. <code>/some/path/</code>,
460     * where icons are held. Subclasses may override this.
461     * @return the icon base
462     * @see #getIcons
463     * @deprecated the function is not consistent with openide's setIconBase() behaviour.
464     * Given the icon badging, there's no need for composing icon names - see
465     * {@link #setBadges}, {@link #getBadges} or {@link #getBareIconBase()}
466     */

467     protected String JavaDoc getIconBase() {
468         return ICON_BASE;
469     }
470     
471     /**
472      * Returns the base resource name for the basic icon that identifies object type
473      * to the user. Additional images may be superimposed over that icon using icon
474      * badging specification. Override in subclasses to provide a different icon for
475      * the node.
476      * @return the icon base
477      */

478     protected String JavaDoc getBareIconBase() {
479         return getIconBase() + getIcons()[0];
480     }
481
482     /** Get the icons.
483     * This should be a list of bare icon names (i.e. no extension or path) in the icon base.
484     * It should contain five icons in order for:
485     * <ul>
486     * <li>a regular class
487     * <li>a class with a main method
488     * <li>a class with a parse error
489     * <li>a JavaBean class
490     * <li>a JavaBean class with a main method
491     * </ul>
492     * Subclasses may override this.
493     * @return the icons
494     * @see #getIconBase
495     * @deprecated State icons are now handled using icon badging mechanism. If you need
496     * to add or modify the badges attached by the java module, override {@link #getBadges}
497     */

498     protected String JavaDoc[] getIcons() {
499         return new String JavaDoc[] { "class" }; // NOI18N
500
}
501
502     /**
503      * Returns badges that should be combined with the base icon.
504      * @return array of badges, can be null if no badges should be used.
505      */

506     protected String JavaDoc[] getBadges() {
507         if (currentBadges.isEmpty())
508             return null;
509         return (String JavaDoc[])currentBadges.toArray(new String JavaDoc[currentBadges.size()]);
510     }
511     
512     /**
513      * Replaces the set of badges applied to the base icon. If the new set differs
514      * from the current one, the new value is recorded and icon property changes are
515      * fired.
516      * @param badges list of badge identifiers; null to clear.
517      */

518     protected void setBadges(String JavaDoc[] badges) {
519         if (badges == null)
520             badges = new String JavaDoc[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 JavaDoc(Arrays.asList(badges));
534         fireIconChange();
535         fireOpenedIconChange();
536     }
537     
538     /** Update the icon for this node based on parse status.
539     * Called automatically at the proper times.
540     * @see #getIconBase
541     * @see #getBadges
542     * @see #setBadges
543     */

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         //TDB - avoid classloading by turning off parsing for templates, bug 28623
560
if (isTemplate)
561             return;
562         //Ignore virtual files #46348
563
if (fo.isVirtual())
564             return;
565
566         final java.util.Collection JavaDoc badges = new java.util.ArrayList JavaDoc(3);
567         final String JavaDoc desc;
568         
569         String JavaDoc pack2 = ""; // NOI18N
570
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 // if (!resource.getErrors().isEmpty()) {
584
// desc = Util.getString("HINT_ParsingErrors");
585
// badges.add(ICON_ATTR_ERROR);
586

587             FileObject parent = fo.getParent();
588             ClassPath cp = ClassPath.getClassPath(parent, ClassPath.SOURCE);
589             String JavaDoc pack = (cp == null) ? null : cp.getResourceName(parent, '.',false); // NOI18N
590
// check the package
591
if (pack == null || !pack.equals(pack2)) {
592                 desc = new MessageFormat JavaDoc(Util.getString("FMT_Bad_Package")).format(new Object JavaDoc[] { pack2 });
593                 //badges.add(ICON_ATTR_ERROR);
594
} 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         // next: check whether the dataobject is up-to-date (if the settings permit that).
610
// don't check it if the node corresponds to a template.
611
if (!isTemplate) {
612             String JavaDoc compBadge = resolveCompileBadge();
613             if (compBadge != null)
614                 badges.add(compBadge);
615         }
616
617         SwingUtilities.invokeLater(new Runnable JavaDoc() {
618             public void run() {
619                 setShortDescription(desc);
620                 if (badges.isEmpty()) {
621                     setBadges(ICON_ATTRS_NONE);
622                 } else {
623                     setBadges((String JavaDoc[])badges.toArray(new String JavaDoc[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 JavaDoc 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 JavaDoc {
662         
663         private static StateUpdater INSTANCE;
664         /** nodes that will be processed in a scheduled {@link #task} */
665         private Set JavaDoc registeredNodes;
666         private final RequestProcessor QUEUE;
667         private RequestProcessor.Task task;
668
669
670         private StateUpdater() {
671             registeredNodes = new HashSet JavaDoc(37);
672             QUEUE = new RequestProcessor("Java Node State Updater"); // NOI18N
673
}
674         
675         /**
676          * registeres nodes to process their icons in async way. {@link JavaNode#resolveIcons()} is the callback used
677          * by the updater that should compute icon.
678          * @param node
679          */

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 JavaDoc nodes;
704             synchronized(this) {
705                 nodes = new ArrayList JavaDoc(this.registeredNodes);
706                 this.registeredNodes.clear();
707             }
708             updateNodes(nodes);
709         }
710
711         private void updateNodes(List JavaDoc nodes) {
712             for (Iterator JavaDoc 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     /**
723      * Listens to all sources that may influence the state of java source presented by the {@link JavaNode}.
724      * Changes of the state are propagated to the {@link StateUpdater}. Each JavaNode should keep own instance of the
725      * propagator (see {@link #getDefault(JavaNode)})
726      */

727     private static final class StatePropagator
728             extends WeakReference JavaDoc
729             implements PropertyChangeListener JavaDoc, ChangeListener JavaDoc, ParsingListener, Runnable JavaDoc {
730
731         private FileBuiltQuery.Status upToDate;
732         private final DataObject dobj;
733         private final PropertyChangeListener JavaDoc 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             // #67841 - weak listening
744
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         /**
751          * factory to create a propagator for particular node
752          * @param node java node
753          * @return the propagator
754          */

755         public static StatePropagator getDefault(JavaNode node) {
756             return new StatePropagator(node);
757         }
758         
759         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
760             Object JavaDoc src = evt.getSource();
761             String JavaDoc 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                 // XXX #47035: workaround that allows to keep fresh FileBuiltQuery.Status even if the file is moved to another package
768
registerBuildStatusListener();
769                 StateUpdater.registerNode(node);
770             }
771         }
772
773         public void stateChanged(ChangeEvent JavaDoc 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                     //do nothing
790
//resource is no more valid
791
}
792             }
793         }
794
795         public void run() {
796             // here goes GC
797
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 JavaDoc
829             implements Runnable JavaDoc, 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