KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > ui > nodes > elements > SourceChildren


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.ui.nodes.elements;
21
22 import java.util.*;
23 import java.util.List JavaDoc;
24 import java.lang.ref.WeakReference JavaDoc;
25
26 import org.openide.nodes.Children;
27 import org.openide.nodes.Node;
28 import org.openide.nodes.FilterNode;
29 import org.openide.cookies.FilterCookie;
30 import org.openide.util.Utilities;
31 import org.openide.ErrorManager;
32 import org.openide.loaders.DataObject;
33 import org.netbeans.jmi.javamodel.*;
34 import org.netbeans.modules.java.ui.nodes.SourceNodeFactory;
35 import org.netbeans.modules.java.ui.nodes.JavaSourceNodeFactory;
36 import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
37 import org.netbeans.api.mdr.events.*;
38
39 import javax.jmi.reflect.JmiException;
40 import javax.jmi.reflect.InvalidObjectException;
41
42 /** Normal implementation of children for source element nodes.
43 * <P>
44 * Ordering and filtering of the children can be customized
45 * using {@link SourceElementFilter}.
46 * {@link FilterCookie} is implemented to provide a means
47 * for user customization of the filter.
48 * <p>The child list listens to changes in the source element, as well as the filter, and
49 * automatically updates itself as appropriate.
50 * <p>A child factory can be used to cause the children list to create
51 * non-default child nodes, if desired, both at the time of the creation
52 * of the children list, and when new children are added.
53 * <p>The children list may be unattached to any source element temporarily,
54 * in which case it will have no children (except possibly an error indicator).
55 *
56 * @author Dafe Simonek, Jan Jancura, Jan Pokorsky
57 */

58 public class SourceChildren extends Children.Keys implements FilterCookie, ChildrenProvider.KeyHandler {
59
60     /** The key describing state of source element */
61     static final Object JavaDoc NOT_KEY = new Object JavaDoc();
62     /** The key describing state of source element */
63     static final Object JavaDoc ERROR_KEY = new Object JavaDoc();
64     /** PACKAGE modifier support */
65     private static int PPP_MASK = SourceElementFilter.PUBLIC +
66             SourceElementFilter.PRIVATE +
67             SourceElementFilter.PROTECTED;
68
69     /** The resource whose subelements are represented. */
70     protected Resource element;
71     /** Filter for elements. Can be <code>null</code>, in which case
72     * modifier filtering is disabled, and ordering may be reset to the default order. */

73     protected SourceElementFilter filter;
74     /** Factory for obtaining class nodes. */
75     protected SourceNodeFactory factory;
76     /** Weak listener to the resource changes. */
77     private JMIListener wElementL;
78     /** Flag saying whether we have our nodes initialized */
79     private boolean nodesInited = false;
80     
81     private final ChildrenProvider chprovider = new ChildrenProvider(this);
82
83     private final ClassesListener CLS_LISTENER = new ClassesListener(this);
84
85     /**
86      * this helps to track the resource identity if the resource gets invalidated.
87      * Can be <code>null</code>.
88      */

89     private DataObject resourceHolder;
90
91     // init ................................................................................
92

93     /** Create a children list with the default factory and no attached source element.
94     */

95     public SourceChildren() {
96         this (JavaSourceNodeFactory.getDefault(), null);
97     }
98
99     /** Create a children list with the default factory.
100     * @param resource source element to attach to, or <code>null</code>
101     */

102     public SourceChildren(Resource resource) {
103         this(JavaSourceNodeFactory.getDefault(), resource);
104     }
105
106     /** Create a children list with no attached source element.
107     * @param factory a factory for creating children
108     */

109     public SourceChildren(SourceNodeFactory factory) {
110         this(factory, null);
111     }
112
113     /** Create a children list.
114     * @param factory a factory for creating children
115     * @param resource source element to attach to, or <code>null</code>
116     */

117     public SourceChildren(SourceNodeFactory factory, Resource resource) {
118         this.element = resource;
119         this.factory = factory;
120         this.filter = new SourceElementFilter();
121         this.resourceHolder = resource != null?
122                 JavaMetamodel.getManager().getDataObject(resource): null;
123     }
124
125
126     // FilterCookie implementation .............................................................
127

128     /* @return The class of currently asociated filter or null
129     * if no filter is asociated with these children.
130     */

131     public Class JavaDoc getFilterClass() {
132         return SourceElementFilter.class;
133     }
134
135     /* @return The filter currently asociated with these children
136     */

137     public Object JavaDoc getFilter() {
138         return filter;
139     }
140
141     /* Sets new filter for these children.
142     * @param filter New filter. Null == disable filtering.
143     */

144     public void setFilter(Object JavaDoc filter) {
145         if (!(filter instanceof SourceElementFilter))
146             throw new IllegalArgumentException JavaDoc();
147
148         this.filter = (SourceElementFilter) filter;
149         // change element nodes according to the new filter
150
if (nodesInited)
151             refreshAllKeys();
152     }
153
154     protected Resource createResource() {
155         return null;
156     }
157
158     // Children implementation ..............................................................
159

160     protected void addNotify () {
161         setKeys( Collections.singletonList(NOT_KEY));
162         ChildrenProvider.RP.post(new Runnable JavaDoc() {
163             public void run() {
164                 final Resource element;
165                 if (SourceChildren.this.element == null) {
166                     element = SourceChildren.this.createResource();
167                 } else {
168                     element = null;
169                 }
170                 setElementImpl(element);
171                 if (element != null) {
172                     // listen to the source element property changes
173
if (wElementL == null) {
174                         wElementL = new JMIListener(SourceChildren.this, (MDRChangeSource) element);
175                     }
176                     ((MDRChangeSource) element).addListener(wElementL);
177                 }
178                 refreshAllKeys();
179                 nodesInited = true;
180             }
181         });
182     }
183
184     protected void removeNotify () {
185         ChildrenProvider.RP.post(new Runnable JavaDoc() {
186             public void run() {
187                 final Resource element = SourceChildren.this.element;
188                 if (element != null) {
189                     ((MDRChangeSource) element).removeListener(wElementL);
190                 }
191                 CLS_LISTENER.updateClasses(Collections.EMPTY_LIST);
192                 chprovider.clear();
193                 nodesInited = false;
194             }
195         });
196     }
197
198     protected Node[] createNodes(Object JavaDoc key) {
199         Node[] nodes;
200         if (NOT_KEY.equals(key)) {
201             nodes = new Node[] {factory.createWaitNode()};
202         } else if (key instanceof Node) {
203             nodes = new Node[] {new FilterNode((Node) key)};
204         } else if (key instanceof Node[]) {
205             Node[] ns = (Node[]) key;
206             nodes = new Node[ns.length];
207             for (int i = 0; i < ns.length; i++) {
208                 Node orig = ns[i];
209                 nodes[i] = orig == null? orig: new FilterNode(orig);
210             }
211         } else if (ERROR_KEY.equals(key)) {
212             nodes = new Node[] {factory.createWaitNode()};
213         } else {
214             // never should get here
215
nodes = new Node[] {factory.createErrorNode()};
216             ErrorManager.getDefault().notify(
217                     ErrorManager.WARNING,
218                     new IllegalStateException JavaDoc("key: " + key) // NOI18N
219
);
220         }
221         return nodes;
222     }
223     
224     private Node[] createNodesImpl(Object JavaDoc key) throws JmiException {
225         // find out the type of the key and create appropriate node
226
Node n;
227         if (key instanceof JavaEnum) {
228             n = factory.createEnumNode((JavaEnum) key);
229         } else if (key instanceof AnnotationType) {
230             n = factory.createAnnotationTypeNode((AnnotationType) key);
231         } else if (key instanceof JavaClass) {
232             n = factory.createClassNode((JavaClass) key);
233         } else if (NOT_KEY.equals(key)) {
234             n = factory.createWaitNode();
235         } else {
236             // never should get here
237
n = factory.createErrorNode();
238         }
239         
240         return new Node[] {n};
241     }
242
243     public Node[] getNodes(boolean optimalResult) {
244         if (!optimalResult || element == null) {
245             return getNodes();
246         }
247         chprovider.waitFinished();
248         return getNodes();
249     }
250
251     public Node findChild(String JavaDoc name) {
252         Node n = super.findChild(name);
253         if (n == null) {
254             chprovider.waitFinished();
255             n = super.findChild(name);
256         }
257         return n;
258     }
259     
260     // main public methods ..................................................................
261

262     /** Get the currently attached source element.
263     * @return the element, or <code>null</code> if unattached
264     */

265     public Resource getElement() {
266         return element;
267     }
268
269     /** Set a new source element to get information about children from.
270     * @param element the new element, or <code>null</code> to detach
271     */

272     public void setElement(final Resource element) {
273         ChildrenProvider.RP.post(new Runnable JavaDoc() {
274             public void run() {
275                 setElementImpl(element);
276             }
277         });
278     }
279     
280     private void setElementImpl(final Resource element) {
281         if (this.element != null) {
282             ((MDRChangeSource) this.element).removeListener(wElementL);
283         }
284         this.element = element;
285         CLS_LISTENER.updateClasses(Collections.EMPTY_LIST);
286         if (element != null) {
287             if (this.resourceHolder == null) {
288                 this.resourceHolder = JavaMetamodel.getManager().getDataObject(element);
289             }
290             if (wElementL == null) {
291                 wElementL = new JMIListener(this, (MDRChangeSource) element);
292             } else {
293                 wElementL.source = (MDRChangeSource) element;
294             }
295             ((MDRChangeSource) element).addListener(wElementL);
296         }
297         // change element nodes according to the new element
298
if (nodesInited) {
299             refreshAllKeys();
300         }
301     }
302
303     // other methods ..........................................................................
304

305
306     /** Updates all the keys (elements) according to the current
307      * filter and ordering.
308      * Method should be run inside ChildrenProvider.RP.
309      */

310     private void refreshAllKeys () {
311         List JavaDoc keys;
312         assert ChildrenProvider.RP.isRequestProcessorThread();
313         if (element == null) {
314             keys = Collections.singletonList(ERROR_KEY);
315             setKeys(keys);
316         } else {
317             if (!nodesInited) {
318                 keys = Collections.singletonList(NOT_KEY);
319                 setKeys(keys);
320             }
321             chprovider.recomputeChildren();
322         }
323     }
324
325     private List JavaDoc collectKeysImpl() {
326         Resource element = this.element;
327         if (element == null) {
328             return Collections.EMPTY_LIST;
329         }
330         int[] order = (filter == null || (filter.getOrder() == null))
331                       ? SourceElementFilter.DEFAULT_ORDER : filter.getOrder();
332         final List JavaDoc keys = new LinkedList();
333         try {
334             JavaMetamodel.getDefaultRepository().beginTrans(false);
335             try {
336                 if (!element.isValid()) {
337                     keys.add(ERROR_KEY);
338                     return keys;
339                 }
340                 // build ordered and filtered keys for the subelements
341
for (int i = 0; i < order.length; i++)
342                     addKeysOfType(element, keys, order[i]);
343             } finally {
344                 JavaMetamodel.getDefaultRepository().endTrans();
345             }
346         } catch (InvalidObjectException ex) {
347             // some element is invalid. MDR will notify listeners about that change later
348
keys.clear();
349             keys.add(ERROR_KEY);
350         } catch (JmiException ex) {
351             ErrorManager.getDefault().notify(ErrorManager.WARNING, ex);
352         }
353         
354         return keys;
355     }
356     
357     /** Filters and adds the keys of specified type to the given
358     * key collection.
359     */

360     private void addKeysOfType(Resource element, Collection keys, final int elementType) {
361         if (elementType == SourceElementFilter.IMPORT) {
362             // PENDING imports are not solved yet...maybe ImportsChildren???
363
//keys.addAll(Arrays.asList(element.getImports()));
364
return;
365         } else {
366             List JavaDoc/*<JavaClass>*/ cls;
367             if ((filter != null) && filter.isAllClasses()) {
368                 cls = SourceEditSupport.getAllClasses(element);
369                 CLS_LISTENER.updateClasses(cls);
370             } else {
371                 cls = element.getClassifiers();
372             }
373             for (Iterator it = cls.iterator(); it.hasNext(); ) {
374                 JavaClass classElement = (JavaClass) it.next();
375                 int modifiers = classElement.getModifiers();
376                 if ((modifiers & PPP_MASK) == 0) modifiers += SourceElementFilter.PACKAGE;
377                 if ((filter.getModifiers () & modifiers) == 0) continue;
378                 if (classElement instanceof JavaEnum) {
379                     if ((elementType & SourceElementFilter.ENUM) != 0) keys.add(classElement);
380                 } else if (classElement.isInterface()) {
381                     if ((elementType & SourceElementFilter.INTERFACE) != 0) keys.add(classElement);
382                 } else
383                     if ((elementType & SourceElementFilter.CLASS) != 0) keys.add(classElement);
384             }
385         }
386     }
387
388     public List JavaDoc collectKeys() {
389         return this.collectKeysImpl();
390     }
391
392     public Node[] prepareNodes(Object JavaDoc key) {
393         return this.createNodesImpl(key);
394     }
395
396     public void presentKeys(List JavaDoc/*<Element>*/ keys, List JavaDoc/*<Node[]>*/ nodes) {
397         setKeys(nodes);
398     }
399
400     // innerclasses ...........................................................................
401

402     /** The listener for listening to the resource changes */
403     private static final class JMIListener extends WeakReference JavaDoc implements MDRChangeListener, Runnable JavaDoc {
404         
405         private MDRChangeSource source;
406         
407         public JMIListener(SourceChildren referent, MDRChangeSource source) {
408             super(referent, Utilities.activeReferenceQueue());
409             this.source = source;
410         }
411
412         public void change(final MDRChangeEvent e) {
413             final SourceChildren sc = (SourceChildren) get();
414             if (sc == null) return;
415             ChildrenProvider.RP.post(new Runnable JavaDoc() {
416                 public void run() {
417                     processChange(e, sc);
418                 }
419             });
420         }
421         
422         private void processChange(MDRChangeEvent e, SourceChildren sc) {
423             if (e instanceof AttributeEvent) {
424                 if (sc.element == null || !sc.element.isValid()) return;
425                 
426                 final AttributeEvent ae = (AttributeEvent) e;
427                 if ("classifiers".equals(ae.getAttributeName()) && sc.nodesInited) { // NOI18N
428
sc.refreshAllKeys();
429                 }
430             } else if (e instanceof InstanceEvent) {
431                 // keeps track of the resource identity
432
InstanceEvent ie = (InstanceEvent) e;
433                 Object JavaDoc o = ie.getInstance();
434                 if (o == sc.element && !sc.element.isValid()) {
435                     DataObject dobj = sc.resourceHolder;
436                     if (dobj != null && dobj.isValid()) {
437                         Resource newRes = JavaMetamodel.getManager().getResource(dobj.getPrimaryFile());
438                         sc.setElement(newRes);
439                     } else {
440                         sc.setElement(null);
441                     }
442                 }
443             }
444         }
445
446         public void run() {
447             source.removeListener(this);
448         }
449
450     }
451
452     /**
453      * ClassesListener listens to all classes (top-level + inner) of the resource.
454      * It is registered in case {@link SourceElementFilter#isAllClasses} is true
455      * (NavigationView). It refreshes source children if some class is added or
456      * removed in the resource.
457      */

458     private static final class ClassesListener implements MDRChangeListener {
459         
460         private SourceChildren sc;
461         
462         /** listened classes */
463         private List JavaDoc/*<JavaClass>*/ classes;
464
465         public ClassesListener(SourceChildren sc) {
466             this.sc = sc;
467             this.classes = Collections.EMPTY_LIST;
468         }
469
470         /**
471          * changes the list of classes the listener listens to. It makes
472          * a diff of new and old lists in order to register/unregister the listener.
473          */

474         public void updateClasses(List JavaDoc/*<JavaClass>*/ classes) {
475             List JavaDoc toAdd = new ArrayList(classes);
476             toAdd.removeAll(this.classes);
477             this.classes.removeAll(classes);
478             addListeners(toAdd);
479             removeListeners(this.classes);
480             this.classes = new ArrayList(classes);
481         }
482         
483         public void change(final MDRChangeEvent e) {
484             ChildrenProvider.RP.post(new Runnable JavaDoc() {
485                 public void run() {
486                     processChange(e, sc);
487                 }
488             });
489         }
490         
491         private void processChange(MDRChangeEvent e, SourceChildren sc) {
492             if (e instanceof AttributeEvent) {
493                 if (sc.element == null) return;
494                 
495                 final AttributeEvent ae = (AttributeEvent) e;
496                 if ("contents".equals(ae.getAttributeName()) && sc.nodesInited) { // NOI18N
497
Element cm = (Element) ae.getOldElement();
498                     cm = (cm == null)? (Element) ae.getNewElement(): cm;
499                     if (cm != null && cm instanceof JavaClass) {
500                         // conntents changed => add/remove JavaClass
501
sc.refreshAllKeys();
502                     }
503                 }
504             }
505         }
506         
507         private void addListeners(List JavaDoc/*<JavaClass>*/ c) {
508             for (Iterator it = c.iterator(); it.hasNext(); ) {
509                 Object JavaDoc o = it.next();
510                 if (!(o instanceof MDRChangeSource))
511                     continue;
512                 MDRChangeSource el = (MDRChangeSource) o;
513                 el.addListener(this);
514             }
515         }
516         
517         private void removeListeners(List JavaDoc/*<JavaClass>*/ c) {
518             for (Iterator it = c.iterator(); it.hasNext(); ) {
519                 Object JavaDoc o = it.next();
520                 if (!(o instanceof MDRChangeSource))
521                     continue;
522                 MDRChangeSource el = (MDRChangeSource) o;
523                 el.removeListener(this);
524             }
525         }
526     }
527
528 }
529
Popular Tags