KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > src > nodes > ElementNode


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.openide.src.nodes;
21
22 import java.awt.datatransfer.Transferable JavaDoc;
23 import java.awt.datatransfer.DataFlavor JavaDoc;
24 import java.beans.*;
25 import java.io.IOException JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.util.ResourceBundle JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.HashSet JavaDoc;
30
31 import org.openide.src.*;
32 import org.openide.nodes.*;
33 import org.openide.util.HelpCtx;
34 import org.openide.util.Mutex;
35 import org.openide.util.NbBundle;
36 import org.openide.util.WeakListeners;
37 import org.openide.util.actions.SystemAction;
38 import org.openide.util.datatransfer.ExTransferable;
39
40 /** Superclass of nodes representing elements in the source hierarchy.
41 * <p>Element nodes generally:
42 * <ul>
43 * <li>Have an associated icon, according to {@link #resolveIconBase}.
44 * <li>Have a display name based on the element's properties, using {@link #elementFormat};
45 * changes to {@link ElementFormat#dependsOnProperty relevant} element properties
46 * automatically affect the display name.
47 * <li>Have some node properties (displayable on the property sheet), according to
48 * the element's properties, and with suitable editors.
49 * <li>Permit renames and deletes, if a member element and writeable.
50 * <li>As permitted by the element, and a writable flag in the node,
51 * permit cut/copy/paste operations, as well as creation of new members.
52 * </ul>
53 *
54 * @author Petr Hamernik
55 */

56 public abstract class ElementNode extends AbstractNode implements IconStrings, ElementProperties {
57     /** Source of the localized human presentable strings. */
58     static ResourceBundle JavaDoc bundle = NbBundle.getBundle(ElementNode.class);
59     
60     private static ElementFormat invalidFormat;
61
62     /** Options for the display name format. */
63     protected static final SourceOptions sourceOptions = (SourceOptions) SourceOptions.findObject(SourceOptions.class, true);
64
65     /** Default return value of getIconAffectingProperties method. */
66     private static final String JavaDoc[] ICON_AFFECTING_PROPERTIES = new String JavaDoc[] {
67                 PROP_MODIFIERS
68             };
69
70     /** Associated element. */
71     protected Element element;
72
73     /** Format for {@link java.beans.FeatureDescriptor#getDisplayName}. */
74     protected ElementFormat elementFormat = new ElementFormat (""); // NOI18N
75

76     /** Is this node read-only or are modifications permitted? */
77     protected boolean writeable;
78
79     /** Listener to forbid its garbage collection */
80     private transient PropertyChangeListener listener;
81
82     /** Create a new element node.
83     *
84     * @param element element to represent
85     * @param children child nodes
86     * @param writeable <code>true</code> if this node should allow modifications.
87     * These include writable properties, clipboard operations, deletions, etc.
88     */

89     public ElementNode(Element element, Children children, boolean writeable) {
90         super(children);
91         this.element = element;
92         this.writeable = writeable;
93         setIconBase(resolveIconBase());
94         setDisplayName(getElementFormat().format(element));
95         listener = createElementListener();
96         element.addPropertyChangeListener(WeakListeners.propertyChange (listener, element));
97         displayFormat = null;
98     }
99
100     /* Gets the short description of this node.
101     * @return A localized short description associated with this node.
102     */

103     public String JavaDoc getShortDescription() {
104         try {
105             return getHintElementFormat().format(element);
106         }
107         catch (IllegalArgumentException JavaDoc e) {
108             return super.getShortDescription();
109         }
110     }
111
112     /** Get the currently appropriate icon base.
113     * Subclasses should make this sensitive to the state of the element--for example,
114     * a private variable may have a different icon than a public one.
115     * The icon will be automatically changed whenever a
116     * {@link #getIconAffectingProperties relevant} change is made to the element.
117     * @return icon base
118     * @see AbstractNode#setIconBase
119     */

120     abstract protected String JavaDoc resolveIconBase();
121
122     /** Get the names of all element properties which might affect the choice of icon.
123     * The default implementation just returns {@link #PROP_MODIFIERS}.
124     * @return the property names, from {@link ElementProperties}
125     */

126     protected String JavaDoc[] getIconAffectingProperties() {
127         return ICON_AFFECTING_PROPERTIES;
128     }
129
130     /** Get a format for the element's display name.
131     * The display name will be automatically updated whenever a
132     * {@link ElementFormat#dependsOnProperty relevant}
133     * change is made to the element.
134     * @return the format
135     */

136     public final ElementFormat getElementFormat() {
137         return elementFormat;
138     }
139
140     /** Set the format for the display name.
141     * @param elementFormat the new format
142     * @throws IllegalArgumentException if the format object is inappropriate
143     * for this type of Element. No assignment is made in such case.
144     */

145     public final void setElementFormat(ElementFormat elementFormat) {
146         setDisplayName(elementFormat.format(ElementNode.this.element));
147         this.elementFormat = elementFormat;
148     }
149     
150     final void setElementFormat0(ElementFormat elementFormat) {
151         try {
152             setElementFormat(elementFormat);
153         } catch (IllegalArgumentException JavaDoc iae) {
154             setElementFormat(getInvalidFormat());
155         }
156     }
157     
158     static ElementFormat getInvalidFormat() {
159         if (invalidFormat != null)
160             return invalidFormat;
161         return invalidFormat = new ElementFormat(bundle.getString("FMT_InvalidFormat")); // NOI18N
162
}
163     
164     /** Get a format for creating this node's
165     * {@link java.beans.FeatureDescriptor#getShortDescription short description}.
166     */

167     abstract protected ElementFormat getHintElementFormat();
168
169     public HelpCtx getHelpCtx () {
170         return new HelpCtx (ElementNode.class);
171     }
172
173     /** Test whether this node can be renamed.
174     * The default implementation assumes it can if this node is {@link #writeable}.
175     *
176     * @return <code>true</code> if this node can be renamed
177     */

178     public boolean canRename() {
179         return isWriteable();
180     }
181
182     /** Test whether this node can be deleted.
183     * The default implementation assumes it can if this node is {@link #writeable}.
184     *
185     * @return <code>true</code> if this node can be renamed
186     */

187     public boolean canDestroy () {
188         return isWriteable();
189     }
190
191     /* Copy this node to the clipboard.
192     *
193     * @return a transferable with node and string copy flavors
194     * @throws IOException if it could not copy
195     */

196     public Transferable JavaDoc clipboardCopy () throws IOException JavaDoc {
197         ExTransferable ex = ExTransferable.create(super.clipboardCopy());
198         ex.put(new ElementStringTransferable());
199         return ex;
200     }
201
202     /* Cut this node to the clipboard.
203     *
204     * @return a transferable with a node cut flavor and a string copy flavor
205     * @throws IOException if it could not cut
206     */

207     public Transferable JavaDoc clipboardCut () throws IOException JavaDoc {
208         if (!isWriteable())
209             throw new IOException JavaDoc();
210
211         ExTransferable ex = ExTransferable.create(super.clipboardCut());
212         // [PENDING] since the user cut, not copied, perhaps it should
213
// not include string copy flavor...?
214
ex.put(new ElementStringTransferable());
215         return ex;
216     }
217
218     /** Transferable for elements as String. */
219     class ElementStringTransferable extends ExTransferable.Single {
220         /** Construct new Transferable for this node. */
221         ElementStringTransferable() {
222             super(DataFlavor.stringFlavor);
223         }
224
225         /** @return the data as String */
226         protected Object JavaDoc getData() {
227             return element.toString();
228         }
229     }
230
231     /** Test whether this node can be copied.
232     * The default implementation returns <code>true</code>.
233     * @return <code>true</code> if it can
234     */

235     public boolean canCopy () {
236         return true;
237     }
238
239     /** Test whether this node can be cut.
240     * The default implementation assumes it can if this node is {@link #writeable}.
241     * @return <code>true</code> if it can
242     */

243     public boolean canCut () {
244         return isWriteable();
245     }
246
247     /** Set all actions for this node.
248     * @param actions new list of actions
249     */

250     public void setActions(SystemAction[] actions) {
251         systemActions = actions;
252     }
253
254     /** Calls super.fireCookieChange. The reason why is redefined
255     * is only to allow the access from this package.
256     */

257     void superFireCookieChange() {
258         fireCookieChange();
259     }
260
261     /** Get a cookie from this node.
262     * First tries the node itself, then {@link Element#getCookie}.
263     * Since {@link Element} implements <code>Node.Cookie</code>, it is
264     * possible to find the element from a node using code such as:
265     * <p><code><pre>
266     * Node someNode = ...;
267     * MethodElement element = (MethodElement) someNode.getCookie (MethodElement.class);
268     * if (element != null) { ... }
269     * </pre></code>
270     * @param type the cookie class
271     * @return the cookie or <code>null</code>
272     */

273     public Node.Cookie getCookie (Class JavaDoc type) {
274         Node.Cookie c = super.getCookie(type);
275         if (c == null)
276             c = element.getCookie(type);
277
278         return c;
279     }
280
281     /** Test for equality.
282     * @return <code>true</code> if the represented {@link Element}s are equal
283     */

284     public boolean equals (Object JavaDoc o) {
285         return (o instanceof ElementNode) && (element.equals (((ElementNode)o).element));
286     }
287
288     /** Get a hash code.
289     * @return the hash code from the represented {@link Element}
290     */

291     public int hashCode () {
292         return element.hashCode ();
293     }
294
295
296     /** Get a handle.
297     * If this is part of a hierarchy and its position can be stored, this is done.
298     * Otherwise the handle will restore using the default factory.
299     */

300     public Node.Handle getHandle () {
301         Node.Handle supe = super.getHandle ();
302         if (supe != null)
303             return supe;
304         else if (element instanceof SourceElement)
305             return null;
306         else
307             return new ElementNodeHandle (element, writeable, elementFormat);
308     }
309
310     private static final class ElementNodeHandle implements Node.Handle {
311         private static final long serialVersionUID = 910667289626540L;
312         private Element element;
313         private boolean writable;
314         private ElementFormat elementFormat;
315         public ElementNodeHandle (Element element, boolean writable, ElementFormat elementFormat) {
316             this.element = element;
317             this.writable = writable;
318             this.elementFormat = elementFormat;
319         }
320         public Node getNode () throws IOException JavaDoc {
321             ElementNodeFactory factory = new DefaultFactory (writable);
322             Node n;
323             if (element instanceof ClassElement)
324                 n = factory.createClassNode ((ClassElement) element);
325             else if (element instanceof ConstructorElement)
326                 n = factory.createConstructorNode ((ConstructorElement) element);
327             else if (element instanceof FieldElement)
328                 n = factory.createFieldNode ((FieldElement) element);
329             else if (element instanceof InitializerElement)
330                 n = factory.createInitializerNode ((InitializerElement) element);
331             else if (element instanceof MethodElement)
332                 n = factory.createMethodNode ((MethodElement) element);
333             else
334                 throw new IOException JavaDoc ("what is element " + element + "? cannot restore node"); // NOI18N
335
if (n instanceof ElementNode)
336                 ((ElementNode) n).setElementFormat (elementFormat);
337             return n;
338         }
339         public String JavaDoc toString () {
340             return "ElementNodeHandle[" + element + "]"; // NOI18N
341
}
342     }
343
344     boolean isWriteable() {
345         return writeable && SourceEditSupport.isWriteable(this.element);
346     }
347
348     void superSetName(String JavaDoc name) {
349         super.setName(name);
350     }
351
352     void superPropertyChange (String JavaDoc name, Object JavaDoc o, Object JavaDoc n) {
353         if (hasPropertyChangeListener()) {
354             if (name == null || findPropertyNames().contains(name)) {
355                 super.firePropertyChange (name, o, n);
356             }
357         }
358     }
359     
360     /**
361      * finds all names of node's properties
362      * @return
363      */

364     private Set JavaDoc findPropertyNames() {
365         Set JavaDoc names = new HashSet JavaDoc();
366         Node.PropertySet[] npsets = getPropertySets();
367         for (int i = 0; i < npsets.length; i++) {
368             Node.PropertySet npset = npsets[i];
369             Node.Property[] nps = npset.getProperties();
370             for (int j = 0; j < nps.length; j++) {
371                 Node.Property np = nps[j];
372                 names.add(np.getName());
373             }
374         }
375         return names;
376     }
377     
378     public Node.PropertySet[] getPropertySets() {
379         return super.getPropertySets();
380     }
381
382     void superShortDescriptionChange (String JavaDoc o, String JavaDoc n) {
383         super.fireShortDescriptionChange(o, n);
384     }
385     
386     PropertyChangeListener createElementListener() {
387     return new ElementListener();
388     }
389
390     // ================== Element listener =================================
391

392     /** Listener for changes of the element's property changes.
393     * It listens and changes updates the iconBase and displayName
394     * if the changed property could affect them.
395     */

396     class ElementListener implements PropertyChangeListener {
397         /** Called when any element's property changed.
398         */

399         public void propertyChange(final PropertyChangeEvent evt) {
400             Mutex.EVENT.writeAccess(new Runnable JavaDoc() {
401                 public void run() {
402                     String JavaDoc propName = evt.getPropertyName();
403                     if (propName == null) {
404                         setDisplayName(getElementFormat().format(ElementNode.this.element));
405                         setIconBase(resolveIconBase());
406                     }
407                     else {
408                         // element went invalid:
409
if (ElementProperties.PROP_VALID.equals(propName)) {
410                             fireNodeDestroyed();
411                             return;
412                         }
413                         // display name
414
if (getElementFormat().dependsOnProperty(propName))
415                             setDisplayName(getElementFormat().format(ElementNode.this.element));
416
417                         // icon
418
String JavaDoc[] iconProps = getIconAffectingProperties();
419                         for (int i = 0; i < iconProps.length; i++) {
420                             if (iconProps[i].equals(propName)) {
421                                 setIconBase(resolveIconBase());
422                                 break;
423                             }
424                         }
425
426                         if (propName.equals(ElementProperties.PROP_NAME)) {
427                             // set inherited name - this code should rather in MemberElementNode,
428
// but we safe one instance of listener for each node
429
// if it will be here. [Petr]
430
try {
431                                 superSetName(((MemberElement)ElementNode.this.element).getName().toString());
432                             }
433                             catch (ClassCastException JavaDoc e) {
434                                 // it is strange - PROP_NAME has only member element.
435
}
436                         }
437                         else {
438                             if (propName.equals(Node.PROP_COOKIE)) {
439                                 // Fires the changes of the cookies of the element.
440
superFireCookieChange();
441                                 return;
442                             }
443                         }
444                     }
445
446                     if (getHintElementFormat().dependsOnProperty(evt.getPropertyName()))
447                         superShortDescriptionChange("", getShortDescription()); // NOI18N
448

449                     superPropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
450                 }
451             });
452         }
453     }
454
455     // ================== Property support for element nodes =================
456

457     /** Property support for element nodes properties.
458     */

459     static abstract class ElementProp extends PropertySupport {
460         /** Constructs a new ElementProp - support for properties of
461         * element hierarchy nodes.
462         *
463         * @param name The name of the property
464         * @param type The class type of the property
465         * @param canW The canWrite flag of the property
466         */

467         public ElementProp(String JavaDoc name, java.lang.Class JavaDoc type, boolean canW) {
468             super(name, type,
469                   bundle.getString("PROP_" + name),
470                   bundle.getString("HINT_" + name),
471                   true, canW);
472         }
473
474         /** Setter for the value. This implementation only tests
475         * if the setting is possible.
476         *
477         * @param val the value of the property
478         * @exception IllegalAccessException when this ElementProp was constructed
479         * like read-only.
480         */

481         public void setValue (Object JavaDoc val) throws IllegalArgumentException JavaDoc,
482             IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
483             if (!canWrite())
484                 throw new IllegalAccessException JavaDoc(bundle.getString("MSG_Cannot_Write"));
485         }
486
487        /** Invokes the runnable using NbDocument.runAtomic.
488         * If BadLocationException occured inside, it will be thrown
489         * the new SourceException.
490         */

491         void runAtomic(Element element, final SourceEditSupport.ExceptionalRunnable exRun) throws InvocationTargetException JavaDoc {
492             try {
493                 SourceEditSupport.runAsUser(element, exRun);
494             } catch (SourceException e) {
495                 throw new InvocationTargetException JavaDoc(e);
496             }
497         }
498     }
499 }
500
Popular Tags