KickJava   Java API By Example, From Geeks To Geeks.

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


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.Component JavaDoc;
23 import java.beans.*;
24 import java.io.IOException JavaDoc;
25 import java.lang.reflect.Modifier JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.util.*;
28 import java.awt.datatransfer.Transferable JavaDoc;
29 import org.openide.DialogDisplayer;
30
31 import org.openide.ErrorManager;
32 import org.openide.src.*;
33 import org.openide.nodes.*;
34 import org.openide.util.NbBundle;
35 import org.openide.util.datatransfer.*;
36 import org.openide.NotifyDescriptor;
37
38
39 /** Node representing a Java class.
40 * @see ClassElement
41 *
42 * @author Petr Hamernik
43 */

44 public class ClassElementNode extends MemberElementNode {
45
46     /** Return value of getIconAffectingProperties method. */
47     private static final String JavaDoc[] ICON_AFFECTING_PROPERTIES = new String JavaDoc[] {
48                 PROP_CLASS_OR_INTERFACE
49             };
50
51     /** Create a new class node.
52     * @param element class element to represent
53     * @param children node children
54     * @param writeable <code>true</code> to be writable
55     */

56     public ClassElementNode(ClassElement element, Children children, boolean writeable) {
57         super(element, children, writeable);
58         setElementFormat0(((ClassElement)element).isInterface() ?
59                           sourceOptions.getInterfaceElementFormat() :
60                           sourceOptions.getClassElementFormat());
61     }
62
63     public org.openide.util.HelpCtx getHelpCtx () {
64         if (((ClassElement)element).isClassOrInterface())
65             return new org.openide.util.HelpCtx ("org.openide.src.nodes.ClassNode"); // NOI18N
66
else
67             return new org.openide.util.HelpCtx ("org.openide.src.nodes.InterfaceNode"); // NOI18N
68
}
69
70     /* Resolve the current icon base.
71     * @return icon base string.
72     */

73     protected String JavaDoc resolveIconBase() {
74         return ((ClassElement)element).isInterface() ? INTERFACE : CLASS;
75     }
76
77     /* This method is used for resolving the names of the properties,
78     * which could affect the icon (such as "modifiers").
79     * @return the appropriate array.
80     */

81     protected String JavaDoc[] getIconAffectingProperties() {
82         return ICON_AFFECTING_PROPERTIES;
83     }
84
85     /* This method resolve the appropriate hint format for the type
86     * of the element. It defines the short description.
87     */

88     protected ElementFormat getHintElementFormat() {
89         return ((ClassElement)element).isInterface() ?
90                sourceOptions.getInterfaceElementLongFormat() :
91                sourceOptions.getClassElementLongFormat();
92     }
93
94     /* Creates property set for this node */
95     protected Sheet createSheet () {
96         Sheet sheet = Sheet.createDefault();
97         Sheet.Set ps = sheet.get(Sheet.PROPERTIES);
98         ps.put(createModifiersProperty(writeable));
99         ps.put(createNameProperty(writeable));
100         if (((ClassElement)element).isClass())
101             ps.put(createSuperclassProperty(writeable));
102         ps.put(createInterfacesProperty(writeable));
103         return sheet;
104     }
105
106     /** Remove this class from its declaring class or source file.
107     *
108     * @exception IOException if the containing element refuses to delete it
109     */

110     public void destroy() throws IOException JavaDoc {
111         SourceEditSupport.invokeAtomicAsUser(element, new SourceEditSupport.ExceptionalRunnable() {
112                                                  public void run() throws SourceException {
113                                                      ClassElement el = (ClassElement) element;
114                                                      if (el.getDeclaringClass() != null) {
115                                                          el.getDeclaringClass().removeClass(el);
116                                                      }
117                                                      else {
118                                                          el.getSource().removeClass(el);
119                                                      }
120                                                  }
121                                              });
122         super.destroy();
123     }
124
125     public Component JavaDoc getCustomizer() {
126         return new ClassCustomizer((ClassElement)element);
127     }
128
129     public boolean hasCustomizer() {
130         return isWriteable();
131     }
132
133     /* Accumulate the paste types that this node can handle
134     * for a given transferable.
135     * <P>
136     * The default implementation simply tests whether the transferable supports
137     * {@link NodeTransfer#nodePasteFlavor}, and if so, it obtains the paste types
138     * from the {@link NodeTransfer.Paste transfer data} and inserts them into the set.
139     *
140     * @param t a transferable containing clipboard data
141     * @param s a set of {@link PasteType}s that will have added to it all types
142     * valid for this node
143     */

144     protected void createPasteTypes (final Transferable JavaDoc t, java.util.List JavaDoc s) {
145         if (isWriteable()) {
146             // special case - multiple source element nodes...
147
if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
148                 createMultiPasteTypes(t, s, NodeTransfer.COPY);
149                 createMultiPasteTypes(t, s, NodeTransfer.MOVE);
150                 return;
151             }
152             for (int i = 0; i <= 1; i++) {
153                 final boolean delete = (i == 1);
154                 final Element addingElement = (Element) NodeTransfer.cookie(t,
155                                               delete ? NodeTransfer.MOVE : NodeTransfer.COPY, Element.class);
156
157                 if (addingElement != null && isValidElement(addingElement)) {
158                     s.add(new PasteType() {
159                               public Transferable JavaDoc paste() throws IOException JavaDoc {
160                                   pasteElement(addingElement, delete);
161                                   return delete ? ExTransferable.EMPTY : null;
162                               }
163                           });
164                 }
165             }
166         }
167         super.createPasteTypes(t, s);
168     }
169     
170     /** Checks if the element is valid. */
171     private static boolean isValidElement(Element el) {
172         // not nice to call spi but there is no way to find out validity of the source element via api.
173
Element.Impl2 impl = (Element.Impl2) el.getCookie(Element.Impl2.class);
174         return impl == null || impl.isValid();
175     }
176     
177     private void createMultiPasteTypes(Transferable JavaDoc t, List s, int action) {
178         MultiTransferObject mto;
179         
180         try {
181             mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor);
182         } catch (java.awt.datatransfer.UnsupportedFlavorException JavaDoc ex) {
183             return;
184         } catch (IOException JavaDoc ex) {
185             return;
186         }
187         
188         int count = mto.getCount();
189         Collection candidates = new LinkedList();
190
191         for (int i = 0; i < count; i++) {
192             Node n = NodeTransfer.node(mto.getTransferableAt(i), action);
193             if (n == null)
194                 break;
195             Element el = (Element)n.getCookie(Element.class);
196             // filter out non-Elements and elements that cannot be pasted
197
// to a class.
198
if (el == null || !isValidElement(el) ||
199                 !(el instanceof MemberElement || el instanceof InitializerElement))
200                 break;
201             // check whether one of the candidates is a parent of the node.
202
// alternatively, the node may be parent of one of the nodes
203
// in candidates.
204
addNodeCandidate(candidates, el);
205         }
206         if (candidates.isEmpty())
207             return;
208         s.add(new SourceEditSupport.ClassMultiPasteType(
209             this, candidates, (action & NodeTransfer.MOVE) > 0));
210     }
211     
212     private void addNodeCandidate(Collection candidates, Element el) {
213         ClassElement enc2 = findEnclosingClass(el);
214         SourceElement enc2Src = enc2.getSource();
215         String JavaDoc fn2 = enc2.getName().getFullName();
216         
217         for (Iterator it = candidates.iterator(); it.hasNext(); ) {
218             Element can = (Element)it.next();
219             ClassElement enc1 = findEnclosingClass(can);
220
221             if (enc1.getSource() != enc2Src) {
222                 continue;
223             }
224             // next, if the enclosing classes are the same...
225
if (enc1 == enc2) {
226                 if (can == enc1) {
227                     // enc2 must be member of enc1, don't add it at all!
228
return;
229                 } else if (el == enc2) {
230                     // can != enc1 -> can is member of enc1.
231
// el == enc2 --> el is declaring class of `can'
232
// replace `can' with `el'.
233
it.remove();
234                     // there can be more such member elements.
235
continue;
236                 } else {
237                     // OK, there is a member of the same class --->
238
// there cannot be an outer class -> OK.
239
break;
240                 }
241             }
242             String JavaDoc fn1 = enc1.getName().getFullName();
243             if (fn2.startsWith(fn1)) {
244                 if (enc1 == can) {
245                     // enc2 is inner class of enc1 --> do *NOT* add this element.
246
return;
247                 } else
248                     continue;
249             } else if (fn1.startsWith(fn2)) {
250                 if (enc2 == el) {
251                     it.remove();
252                     continue;
253                 } else
254                     break;
255             }
256         }
257         candidates.add(el);
258     }
259     
260     private ClassElement findEnclosingClass(Element el) {
261         if (el instanceof ClassElement)
262             return (ClassElement)el;
263         else if (el instanceof MemberElement)
264             return ((MemberElement)el).getDeclaringClass();
265         else if (el instanceof InitializerElement)
266             return ((InitializerElement)el).getDeclaringClass();
267         else
268             return null;
269     }
270     
271     PropertyChangeListener createElementListener() {
272     return new ClassElementListener();
273     }
274
275     /** Paste element into this class.
276     * @param addingElement Element to add.
277     * @param delete Whether element should be deleted from the original class
278     * @exception IOException if any proble occured
279     */

280     void pasteElement(final Element addingElement, final boolean delete) throws IOException JavaDoc {
281         final boolean[] cancelled = {false};
282     
283     // verify that the source does not enclose the target -- there's risk of recursion or
284
// other errors.
285
ClassElement declClazz, myDecl;
286
287     if (addingElement instanceof ClassElement) {
288         declClazz = (ClassElement)addingElement;
289         for (myDecl = (ClassElement)element; myDecl != null; myDecl = myDecl.getDeclaringClass()) {
290         if (declClazz == myDecl) {
291             throw (IOException JavaDoc)ErrorManager.getDefault().annotate(
292             new IOException JavaDoc("Recursion detected"), // NOI18N
293
bundle.getString("ERR_RecursePaste")
294             );
295         }
296         }
297     }
298         SourceEditSupport.invokeAtomicAsUser(element, new SourceEditSupport.ExceptionalRunnable() {
299              public void run() throws SourceException {
300                  ClassElement clazz = (ClassElement) element;
301                  if (addingElement instanceof InitializerElement) {
302                      InitializerElement e = (InitializerElement)addingElement;
303                      clazz.addInitializer(e);
304                  }
305                  if (addingElement instanceof FieldElement) {
306                      clazz.addField((FieldElement)addingElement);
307                  }
308                  else if (addingElement instanceof MethodElement) {
309                      MethodElement me = (MethodElement) addingElement;
310                      if (((ClassElement) element).isInterface()) {
311                          if (delete
312                                && (me.getBody() != null)
313                                && (!me.getBody().trim().equals("")) // NOI18N
314
&& (!isPastingConfirmed(me))) {
315                             cancelled[0] = true;
316                             return;
317                          }
318                          me = (MethodElement) me.clone();
319                          me.setBody(null);
320                      } else if (me.getBody() == null) {
321                             me = (MethodElement) me.clone();
322                             me.setBody(""); // NOI18N
323
}
324                      clazz.addMethod(me);
325                  }
326                  else if (addingElement instanceof ConstructorElement) {
327                      clazz.addConstructor((ConstructorElement)addingElement);
328                  }
329                  else if (addingElement instanceof ClassElement) {
330                      ClassElement pclass = (ClassElement)addingElement;
331                      ClassElement myClass;
332                      
333                      clazz.addClass(pclass);
334                      myClass = clazz.getClass(Identifier.create(pclass.getName().getName()));
335                      if (pclass.getDeclaringClass() == null && myClass != null) {
336                          // if top-level class is pasted as an inner class,
337
// maintain the static modifier.
338
myClass.setModifiers(myClass.getModifiers() | Modifier.STATIC);
339                      }
340                  }
341              }
342         });
343         if (delete && (!cancelled[0])) {
344             final ClassElement origClazz;
345             SourceElement src = null;
346
347             if (addingElement instanceof InitializerElement) {
348                 origClazz = ((InitializerElement)addingElement).getDeclaringClass();
349             } else if (addingElement instanceof MemberElement) {
350                 origClazz = ((MemberElement)addingElement).getDeclaringClass();
351                 if (addingElement instanceof ClassElement) {
352             ClassElement me = (ClassElement)addingElement;
353                     src = me.getSource();
354         }
355             } else {
356                 origClazz = null;
357             }
358         
359         if (src == null && origClazz != null) {
360         src = origClazz.getSource();
361         }
362
363             final SourceElement classSource = src;
364             SourceEditSupport.ExceptionalRunnable r = new SourceEditSupport.ExceptionalRunnable() {
365                  public void run() throws SourceException {
366                     if (addingElement instanceof InitializerElement) {
367                         InitializerElement e = (InitializerElement)addingElement;
368                         if (origClazz != null)
369                             origClazz.removeInitializer(e);
370                     } else if (addingElement instanceof MemberElement) {
371                         if (origClazz != null) {
372                             if (addingElement instanceof FieldElement) {
373                                 origClazz.removeField((FieldElement)addingElement);
374                             } else if (addingElement instanceof MethodElement) {
375                                 origClazz.removeMethod((MethodElement)addingElement);
376                             } else if (addingElement instanceof ConstructorElement) {
377                                 origClazz.removeConstructor((ConstructorElement)addingElement);
378                             } else if (addingElement instanceof ClassElement) {
379                                 origClazz.removeClass((ClassElement)addingElement);
380                             }
381                         } else if ((addingElement instanceof ClassElement) &&
382                                 classSource != null) {
383                             classSource.removeClass((ClassElement)addingElement);
384                         }
385                     }
386                 }
387         };
388         
389         if (src == null) {
390             try {
391                 r.run();
392             } catch (SourceException e) {
393                 throw new IOException JavaDoc(e.getMessage());
394             }
395         } else {
396             SourceEditSupport.invokeAtomicAsUser(addingElement, r);
397             }
398          }
399     }
400     
401     /**
402      * Creates a dialog warning the user that the body of the method which
403      * is to be cut and pasted from a class into an interface will be lost.
404      *
405      * @param me the cut and pasted method
406      * @return whether the user confirmed the action.
407      */

408     private boolean isPastingConfirmed(MethodElement me) {
409         String JavaDoc title = NbBundle.getMessage(ClassElementNode.class,
410         "TIT_PastingMethod"); // NOI18N
411
String JavaDoc text = NbBundle.getMessage(ClassElementNode.class,
412         "CONFIRM_DeleteMethodBody", me.getName()); // NOI18N
413

414         NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
415                 text, title, NotifyDescriptor.YES_NO_OPTION);
416         return NotifyDescriptor.YES_OPTION.equals
417                 (DialogDisplayer.getDefault().notify(desc));
418                                
419     }
420
421
422     /** Create a node property for the superclass of this class.
423     * @param canW if <code>false</code>, property will be read-only
424     * @return the property
425     */

426     protected Node.Property createSuperclassProperty(boolean canW) {
427         return new ElementProp(PROP_SUPERCLASS, String JavaDoc.class, canW) {
428                    /** Gets the value */
429                    public Object JavaDoc getValue () {
430                        Identifier id = ((ClassElement)element).getSuperclass();
431                        return id == null ? "" : id.getFullName(); // NOI18N
432
}
433
434                    /** Sets the value */
435                    public void setValue(final Object JavaDoc val) throws IllegalArgumentException JavaDoc,
436                        IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
437                        super.setValue(val);
438                        if (!(val instanceof String JavaDoc))
439                            throw new IllegalArgumentException JavaDoc();
440                        final String JavaDoc str = ((String JavaDoc)val).trim();
441                        if (str != null && !"".equals(str)) {
442                            Type t = Type.parse(str);
443                            if (!t.isClass())
444                                throw new IllegalArgumentException JavaDoc();
445                        }
446
447                        runAtomic(element, new SourceEditSupport.ExceptionalRunnable() {
448                                      public void run() throws SourceException {
449                                          Identifier superclass = str.equals("") ? null: Identifier.create(str); // NOI18N
450
((ClassElement)element).setSuperclass(superclass);
451                                      }
452                                  });
453                    }
454                };
455     }
456
457     /** Create a node property for the implemented interfaces of this class.
458     * (Or, extended interfaces if this is itself an interface.)
459     * @param canW if <code>false</code>, property will be read-only
460     * @return the property
461     */

462     protected Node.Property createInterfacesProperty(boolean canW) {
463         ElementProp prop = new ElementProp(PROP_INTERFACES, Identifier[].class, canW) {
464                                /** Gets the value */
465                                public Object JavaDoc getValue () {
466                                    return ((ClassElement)element).getInterfaces();
467                                }
468
469                                /** Sets the value */
470                                public void setValue(final Object JavaDoc val) throws IllegalArgumentException JavaDoc,
471                                    IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
472                                    super.setValue(val);
473                                    if (!(val instanceof Identifier[]))
474                                        throw new IllegalArgumentException JavaDoc();
475
476                                    runAtomic(element, new SourceEditSupport.ExceptionalRunnable() {
477                                                  public void run() throws SourceException {
478                                                      ((ClassElement)element).setInterfaces((Identifier[])val);
479                                                  }
480                                              });
481                                }
482                            };
483
484         if (((ClassElement)element).isInterface()) {
485             prop.setDisplayName(bundle.getString("PROP_superInterfaces"));
486             prop.setShortDescription(bundle.getString("HINT_superInterfaces"));
487         }
488         prop.setValue("changeImmediate" /* PropertyEnv.PROP_CHANGE_IMMEDIATE */,Boolean.FALSE); // NOI18N
489
return prop;
490     }
491
492     public NewType[] getNewTypes() {
493         if (writeable) {
494             return SourceEditSupport.createNewTypes((ClassElement)element);
495         } else {
496             return super.getNewTypes();
497         }
498     }
499     
500     public Transferable JavaDoc clipboardCopy() {
501         Transferable JavaDoc t = NodeTransfer.transferable(this, NodeTransfer.CLIPBOARD_COPY);
502         ExTransferable xt = ExTransferable.create(t);
503         xt.put(NodeTransfer.createPaste(new SourceEditSupport.PackagePaste(
504             (ClassElement)this.element, false
505         )));
506         return xt;
507     }
508
509     public Transferable JavaDoc clipboardCut() {
510         Transferable JavaDoc t = NodeTransfer.transferable(this, NodeTransfer.CLIPBOARD_CUT);
511         ExTransferable xt = ExTransferable.create(t);
512         xt.put(NodeTransfer.createPaste(new SourceEditSupport.PackagePaste(
513             (ClassElement)this.element, true
514         )));
515         return xt;
516     }
517     
518     private class ClassElementListener extends ElementListener {
519         public void propertyChange(PropertyChangeEvent evt) {
520             if (evt.getPropertyName().equals(ElementProperties.PROP_CLASS_OR_INTERFACE)) {
521                 setElementFormat(((ClassElement)element).isClass() ?
522                 sourceOptions.getClassElementFormat() :
523                 sourceOptions.getInterfaceElementFormat());
524             }
525             super.propertyChange(evt);
526         }
527     }
528 }
529
Popular Tags