1 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 ; 33 import java.io.IOException ; 34 import java.util.Arrays ; 35 import java.util.HashSet ; 36 import java.util.List ; 37 import java.util.Set ; 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 ; 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 70 public class GoToSupport { 71 72 73 public GoToSupport() { 74 } 75 76 private static FileObject getFileObject(Document doc) { 77 DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty); 78 79 return od != null ? od.getPrimaryFile() : null; 80 } 81 82 public static String getGoToElementTooltip(Document 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 performGoTo(final Document 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 [] result = new String [1]; 99 100 js.runUserActionTask(new CancellableTask<CompilationController>() { 101 public void cancel() { 102 } 103 public void run(CompilationController controller) throws Exception { 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 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 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 ioe) { 230 throw new IllegalStateException (ioe); 231 } 232 } 233 234 public static void goTo(Document doc, int offset, boolean goToSource) { 235 performGoTo(doc, offset, goToSource, false); 236 } 237 238 private static final Set <JavaTokenId> USABLE_TOKEN_IDS = new HashSet <JavaTokenId>(Arrays.asList(JavaTokenId.IDENTIFIER, JavaTokenId.THIS, JavaTokenId.SUPER)); 239 240 public static int[] getIdentifierSpan(Document doc, int offset, Token<JavaTokenId>[] token) { 241 if (getFileObject(doc) == null) { 242 return null; 244 } 245 246 TokenHierarchy<Document > 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 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; } 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 , Element> { 314 315 private CompilationInfo info; 316 private Tree found; 317 318 public @Override Void visitClass(ClassTree node, Element p) { 319 return null; 321 } 322 323 public @Override Void 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 > { 336 337 @Override 338 public TreePath visitMethodInvocation(MethodInvocationTree tree, Void 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 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 , Boolean > { 358 359 private StringBuffer result = new StringBuffer (); 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 visitPackage(PackageElement e, Boolean highlightName) { 374 boldStartCheck(highlightName); 375 376 result.append(e.getQualifiedName()); 377 378 boldStopCheck(highlightName); 379 380 return null; 381 } 382 383 public Void visitType(TypeElement e, Boolean 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 visitVariable(VariableElement e, Boolean 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 result.append(Utilities.getTypeName(enclosing.asType(), true)); 440 } 441 } 442 443 return null; 444 } 445 446 public Void visitExecutable(ExecutableElement e, Boolean 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 } 473 return null; 474 } 475 476 public Void visitTypeParameter(TypeParameterElement e, Boolean highlightName) { 477 return null; 478 } 479 480 private void modifier(Set <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 498 private void dumpTypeArguments(List <? 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 <? 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 <? 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 |