KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > java > GoToSupport


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.editor.java;
21
22 import com.sun.source.tree.ClassTree;
23 import com.sun.source.tree.IdentifierTree;
24 import com.sun.source.tree.MethodInvocationTree;
25 import com.sun.source.tree.NewClassTree;
26 import com.sun.source.tree.ParameterizedTypeTree;
27 import com.sun.source.tree.Tree;
28 import com.sun.source.tree.Tree.Kind;
29 import com.sun.source.tree.VariableTree;
30 import com.sun.source.util.TreePath;
31 import com.sun.source.util.TreePathScanner;
32 import java.awt.Toolkit JavaDoc;
33 import java.io.IOException JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.HashSet JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Set JavaDoc;
38 import javax.lang.model.element.Element;
39 import javax.lang.model.element.ElementKind;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.Modifier;
42 import javax.lang.model.element.PackageElement;
43 import javax.lang.model.element.TypeElement;
44 import javax.lang.model.element.TypeParameterElement;
45 import javax.lang.model.element.VariableElement;
46 import javax.lang.model.type.DeclaredType;
47 import javax.lang.model.type.TypeKind;
48 import javax.lang.model.type.TypeMirror;
49 import javax.lang.model.util.AbstractElementVisitor6;
50 import javax.swing.text.Document JavaDoc;
51 import org.netbeans.api.java.lexer.JavaTokenId;
52 import org.netbeans.api.java.source.CancellableTask;
53 import org.netbeans.api.java.source.ClasspathInfo;
54 import org.netbeans.api.java.source.CompilationController;
55 import org.netbeans.api.java.source.CompilationInfo;
56 import org.netbeans.api.java.source.JavaSource;
57 import org.netbeans.api.java.source.JavaSource.Phase;
58 import org.netbeans.api.java.source.SourceUtils;
59 import org.netbeans.api.java.source.UiUtils;
60 import org.netbeans.api.lexer.Token;
61 import org.netbeans.api.lexer.TokenHierarchy;
62 import org.netbeans.api.lexer.TokenSequence;
63 import org.openide.filesystems.FileObject;
64 import org.openide.loaders.DataObject;
65
66 /**
67  *
68  * @author Jan Lahoda
69  */

70 public class GoToSupport {
71     
72     /** Creates a new instance of GoToSupport */
73     public GoToSupport() {
74     }
75     
76     private static FileObject getFileObject(Document JavaDoc doc) {
77         DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty);
78         
79         return od != null ? od.getPrimaryFile() : null;
80     }
81     
82     public static String JavaDoc getGoToElementTooltip(Document JavaDoc doc, final int offset, final boolean goToSource) {
83         return performGoTo(doc, offset, goToSource, true);
84     }
85     
86     private static boolean isError(Element el) {
87         return el == null || el.asType() == null || el.asType().getKind() == TypeKind.ERROR;
88     }
89     
90     private static String JavaDoc performGoTo(final Document JavaDoc doc, final int offset, final boolean goToSource, final boolean tooltip) {
91         try {
92             final FileObject fo = getFileObject(doc);
93             
94             if (fo == null)
95                 return null;
96             
97             JavaSource js = JavaSource.forFileObject(fo);
98             final String JavaDoc[] result = new String JavaDoc[1];
99             
100             js.runUserActionTask(new CancellableTask<CompilationController>() {
101                 public void cancel() {
102                 }
103                 public void run(CompilationController controller) throws Exception JavaDoc {
104                     if (controller.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
105                         return;
106                     
107                     Token<JavaTokenId>[] token = new Token[1];
108                     int[] span = getIdentifierSpan(doc, offset, token);
109                     
110                     if (span == null) {
111                         Toolkit.getDefaultToolkit().beep();
112                         return ;
113                     }
114                     
115                     int exactOffset = span[0] + 1;
116                     
117                     TreePath path = controller.getTreeUtilities().pathFor(exactOffset);
118                     TreePath parent = path.getParentPath();
119                     Tree parentLeaf = parent.getLeaf();
120                     
121                     if (parentLeaf.getKind() == Kind.NEW_CLASS && ((NewClassTree) parentLeaf).getIdentifier() == path.getLeaf()) {
122                         if (!isError(controller.getTrees().getElement(path.getParentPath()))) {
123                             path = path.getParentPath();
124                         }
125                     } else {
126                         if ( parentLeaf.getKind() == Kind.PARAMETERIZED_TYPE
127                             && parent.getParentPath().getLeaf().getKind() == Kind.NEW_CLASS
128                             && ((ParameterizedTypeTree) parentLeaf).getType() == path.getLeaf()) {
129                             if (!isError(controller.getTrees().getElement(parent.getParentPath()))) {
130                                 path = parent.getParentPath();
131                             }
132                         }
133                     }
134                     
135                     Element el = controller.getTrees().getElement(path);
136                     
137                     if (isError(el)) {
138                         if (!tooltip)
139                             CALLER.beep();
140                         else
141                             result[0] = null;
142                         return;
143                     }
144                     
145                     if (goToSource) {
146                         TypeMirror type = null;
147                         
148                         if (el instanceof VariableElement)
149                             type = el.asType();
150                         
151                         if (type != null && type.getKind() == TypeKind.DECLARED) {
152                             el = ((DeclaredType)type).asElement();
153                         }
154                     }
155                     
156                     if (isError(el)) {
157                         if (!tooltip)
158                             CALLER.beep();
159                         else
160                             result[0] = null;
161                         return;
162                     }
163                     
164                     if (controller.getElementUtilities().isSynthetic(el) && el.getKind() == ElementKind.CONSTRUCTOR) {
165                         //check for annonymous innerclasses:
166
el = handlePossibleAnonymousInnerClass(controller, el);
167                     }
168                     
169                     if (isError(el)) {
170                         if (!tooltip)
171                             CALLER.beep();
172                         else
173                             result[0] = null;
174                         return;
175                     }
176                     
177                     if (el.getKind() != ElementKind.CONSTRUCTOR && (token[0].id() == JavaTokenId.SUPER || token[0].id() == JavaTokenId.THIS)) {
178                         if (!tooltip)
179                             CALLER.beep();
180                         else
181                             result[0] = null;
182                         return;
183                     }
184                     
185                     if (tooltip) {
186                         DisplayNameElementVisitor v = new DisplayNameElementVisitor();
187                         
188                         v.visit(el, true);
189                         
190                         result[0] = "<html><body>" + v.result.toString();
191                     } else {
192                         Tree tree = controller.getTrees().getTree(el);
193                         
194                         if (tree == null && (el.getKind() == ElementKind.PARAMETER || el.getKind() == ElementKind.LOCAL_VARIABLE)) {
195                             while (path.getLeaf().getKind() != Kind.METHOD && path.getLeaf().getKind() != Kind.CLASS) {
196                                 path = path.getParentPath();
197                             }
198                             
199                             FindVariableDeclarationVisitor v = new FindVariableDeclarationVisitor();
200                             
201                             v.info = controller;
202                             v.scan(path, el);
203                             
204                             tree = v.found;
205                         }
206                         
207                         if (tree != null) {
208                             long startPos = controller.getTrees().getSourcePositions().getStartPosition(controller.getCompilationUnit(), tree);
209                             long endPos = controller.getTrees().getSourcePositions().getEndPosition(controller.getCompilationUnit(), tree);
210                             
211                             if (startPos != (-1)) {
212                                 //check if the caret is inside the declaration itself, as jump in this case is not very usefull:
213
if (startPos <= offset && offset <= endPos) {
214                                     CALLER.beep();
215                                 } else {
216                                     CALLER.open(fo, (int) startPos);
217                                 }
218                             } else {
219                                 CALLER.beep();
220                             }
221                         } else {
222                             CALLER.open(controller.getClasspathInfo(), el);
223                         }
224                     }
225                 }
226             },true);
227             
228             return result[0];
229         } catch (IOException JavaDoc ioe) {
230             throw new IllegalStateException JavaDoc(ioe);
231         }
232     }
233     
234     public static void goTo(Document JavaDoc doc, int offset, boolean goToSource) {
235         performGoTo(doc, offset, goToSource, false);
236     }
237     
238     private static final Set JavaDoc<JavaTokenId> USABLE_TOKEN_IDS = new HashSet JavaDoc<JavaTokenId>(Arrays.asList(JavaTokenId.IDENTIFIER, JavaTokenId.THIS, JavaTokenId.SUPER));
239     
240     public static int[] getIdentifierSpan(Document JavaDoc doc, int offset, Token<JavaTokenId>[] token) {
241         if (getFileObject(doc) == null) {
242             //do nothing if FO is not attached to the document - the goto would not work anyway:
243
return null;
244         }
245         
246         TokenHierarchy<Document JavaDoc> th = TokenHierarchy.get(doc);
247         TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId.language());
248         
249         if (ts == null)
250             return null;
251         
252         ts.move(offset);
253         if (!ts.moveNext())
254             return null;
255         
256         Token<JavaTokenId> t = ts.token();
257         
258         if (!USABLE_TOKEN_IDS.contains(t.id())) {
259             ts.move(offset - 1);
260             if (!ts.moveNext())
261                 return null;
262             t = ts.token();
263             if (!USABLE_TOKEN_IDS.contains(t.id()))
264                 return null;
265         }
266         
267         if (token != null)
268             token[0] = t;
269         
270         return new int [] {ts.offset(), ts.offset() + t.length()};
271     }
272     
273     private static Element handlePossibleAnonymousInnerClass(CompilationInfo info, final Element el) {
274         Element encl = el.getEnclosingElement();
275         Element doubleEncl = encl != null ? encl.getEnclosingElement() : null;
276         
277         if ( doubleEncl != null
278             && !doubleEncl.getKind().isClass()
279             && !doubleEncl.getKind().isInterface()
280             && doubleEncl.getKind() != ElementKind.PACKAGE
281             && encl.getKind() == ElementKind.CLASS) {
282             TreePath enclTreePath = info.getTrees().getPath(encl);
283             Tree enclTree = enclTreePath != null ? enclTreePath.getLeaf() : null;
284             
285             if (enclTree != null && enclTree.getKind() == Tree.Kind.CLASS && enclTreePath.getParentPath().getLeaf().getKind() == Tree.Kind.NEW_CLASS) {
286                 NewClassTree nct = (NewClassTree) enclTreePath.getParentPath().getLeaf();
287                 
288                 if (nct.getClassBody() != null) {
289                     Element parentElement = info.getTrees().getElement(new TreePath(enclTreePath, nct.getIdentifier()));
290                     
291                     if (parentElement == null || parentElement.getKind().isInterface()) {
292                         return parentElement;
293                     } else {
294                         //annonymous innerclass extending a class. Find out which constructor is used:
295
TreePath superConstructorCall = new FindSuperConstructorCall().scan(enclTreePath, null);
296                         
297                         if (superConstructorCall != null) {
298                             return info.getTrees().getElement(superConstructorCall);
299                         }
300                     }
301                 }
302             }
303             
304             return null;//prevent jumps to incorrect positions
305
} else {
306             if (encl != null)
307                 return encl;
308             else
309                 return el;
310         }
311     }
312     
313     private static final class FindVariableDeclarationVisitor extends TreePathScanner<Void JavaDoc, Element> {
314         
315         private CompilationInfo info;
316         private Tree found;
317         
318         public @Override JavaDoc Void JavaDoc visitClass(ClassTree node, Element p) {
319             //do not dive into the innerclasses:
320
return null;
321         }
322         
323         public @Override JavaDoc Void JavaDoc visitVariable(VariableTree node, Element p) {
324             Element resolved = info.getTrees().getElement(getCurrentPath());
325             
326             if (resolved == p) {
327                 found = node;
328             }
329             
330             return null;
331         }
332         
333     }
334     
335     private static final class FindSuperConstructorCall extends TreePathScanner<TreePath, Void JavaDoc> {
336         
337         @Override JavaDoc
338         public TreePath visitMethodInvocation(MethodInvocationTree tree, Void JavaDoc v) {
339             if (tree.getMethodSelect().getKind() == Kind.IDENTIFIER && "super".equals(((IdentifierTree) tree.getMethodSelect()).getName().toString())) {
340                 return getCurrentPath();
341             }
342             
343             return null;
344         }
345         
346         @Override JavaDoc
347         public TreePath reduce(TreePath first, TreePath second) {
348             if (first == null) {
349                 return second;
350             } else {
351                 return first;
352             }
353         }
354         
355     }
356     
357     private static final class DisplayNameElementVisitor extends AbstractElementVisitor6<Void JavaDoc, Boolean JavaDoc> {
358         
359         private StringBuffer JavaDoc result = new StringBuffer JavaDoc();
360         
361         private void boldStartCheck(boolean highlightName) {
362             if (highlightName) {
363                 result.append("<b>");
364             }
365         }
366         
367         private void boldStopCheck(boolean highlightName) {
368             if (highlightName) {
369                 result.append("</b>");
370             }
371         }
372         
373         public Void JavaDoc visitPackage(PackageElement e, Boolean JavaDoc highlightName) {
374             boldStartCheck(highlightName);
375             
376             result.append(e.getQualifiedName());
377             
378             boldStopCheck(highlightName);
379             
380             return null;
381         }
382
383         public Void JavaDoc visitType(TypeElement e, Boolean JavaDoc highlightName) {
384             modifier(e.getModifiers());
385             switch (e.getKind()) {
386                 case CLASS:
387                     result.append("class ");
388                     break;
389                 case INTERFACE:
390                     result.append("interface ");
391                     break;
392                 case ENUM:
393                     result.append("enum ");
394                     break;
395                 case ANNOTATION_TYPE:
396                     result.append("@interface ");
397                     break;
398             }
399             Element enclosing = e.getEnclosingElement();
400             
401             if (enclosing == SourceUtils.getEnclosingTypeElement(e)) {
402                 result.append(e.getQualifiedName());
403                 result.append('.');
404                 boldStartCheck(highlightName);
405                 result.append(e.getSimpleName());
406                 boldStopCheck(highlightName);
407             } else {
408                 result.append(e.getQualifiedName());
409             }
410             
411             return null;
412         }
413
414         public Void JavaDoc visitVariable(VariableElement e, Boolean JavaDoc highlightName) {
415             modifier(e.getModifiers());
416             
417             result.append(Utilities.getTypeName(e.asType(), true));
418             
419             result.append(' ');
420             
421             boldStartCheck(highlightName);
422
423             result.append(e.getSimpleName());
424             
425             boldStopCheck(highlightName);
426             
427             if (highlightName) {
428                 if (e.getConstantValue() != null) {
429                     result.append(" = ");
430                     result.append(e.getConstantValue().toString());
431                 }
432                 
433                 result.append(" in ");
434                 
435                 Element enclosing = e.getEnclosingElement();
436                 
437                 if (!(enclosing.getKind() == ElementKind.PARAMETER || enclosing.getKind() == ElementKind.LOCAL_VARIABLE)) {
438                     //short typename:
439
result.append(Utilities.getTypeName(enclosing.asType(), true));
440                 }
441             }
442             
443             return null;
444         }
445
446         public Void JavaDoc visitExecutable(ExecutableElement e, Boolean JavaDoc highlightName) {
447             switch (e.getKind()) {
448                 case CONSTRUCTOR:
449                     modifier(e.getModifiers());
450                     dumpTypeArguments(e.getTypeParameters());
451                     result.append(' ');
452                     boldStartCheck(highlightName);
453                     result.append(e.getSimpleName());
454                     boldStopCheck(highlightName);
455                     dumpArguments(e.getParameters());
456                     dumpThrows(e.getThrownTypes());
457                     break;
458                 case METHOD:
459                     modifier(e.getModifiers());
460                     dumpTypeArguments(e.getTypeParameters());
461                     result.append(Utilities.getTypeName(e.getReturnType(), true));
462                     result.append(' ');
463                     boldStartCheck(highlightName);
464                     result.append(e.getSimpleName());
465                     boldStopCheck(highlightName);
466                     dumpArguments(e.getParameters());
467                     dumpThrows(e.getThrownTypes());
468                     break;
469                 case INSTANCE_INIT:
470                 case STATIC_INIT:
471                     //these two cannot be referenced anyway...
472
}
473             return null;
474         }
475
476         public Void JavaDoc visitTypeParameter(TypeParameterElement e, Boolean JavaDoc highlightName) {
477             return null;
478         }
479         
480         private void modifier(Set JavaDoc<Modifier> modifiers) {
481             boolean addSpace = false;
482             
483             for (Modifier m : modifiers) {
484                 if (addSpace) {
485                     result.append(' ');
486                 }
487                 addSpace = true;
488                 result.append(m.toString());
489             }
490             
491             if (addSpace) {
492                 result.append(' ');
493             }
494         }
495         
496 // private void throwsDump()
497

498         private void dumpTypeArguments(List JavaDoc<? extends TypeParameterElement> list) {
499             if (list.isEmpty())
500                 return ;
501             
502             boolean addSpace = false;
503             
504             result.append('<');
505             
506             for (TypeParameterElement e : list) {
507                 if (addSpace) {
508                     result.append(", ");
509                 }
510                 
511                 result.append(Utilities.getTypeName(e.asType(), true));
512                 
513                 addSpace = true;
514             }
515                 
516             result.append('>');
517         }
518
519         private void dumpArguments(List JavaDoc<? extends VariableElement> list) {
520             boolean addSpace = false;
521             
522             result.append('(');
523             
524             for (VariableElement e : list) {
525                 if (addSpace) {
526                     result.append(", ");
527                 }
528                 
529                 visit(e, false);
530                 
531                 addSpace = true;
532             }
533                 
534             result.append(')');
535         }
536
537         private void dumpThrows(List JavaDoc<? extends TypeMirror> list) {
538             if (list.isEmpty())
539                 return ;
540             
541             boolean addSpace = false;
542             
543             result.append(" throws ");
544             
545             for (TypeMirror t : list) {
546                 if (addSpace) {
547                     result.append(", ");
548                 }
549                 
550                 result.append(Utilities.getTypeName(t, true));
551                 
552                 addSpace = true;
553             }
554         }
555             
556     }
557     
558     static UiUtilsCaller CALLER = new UiUtilsCaller() {
559         public void open(FileObject fo, int pos) {
560             UiUtils.open(fo, pos);
561         }
562         public void beep() {
563             Toolkit.getDefaultToolkit().beep();
564         }
565         public void open(ClasspathInfo info, Element el) {
566             UiUtils.open(info, el);
567         }
568     };
569     
570     interface UiUtilsCaller {
571         public void open(FileObject fo, int pos);
572         public void beep();
573         public void open(ClasspathInfo info, Element el);
574     }
575 }
576
Popular Tags