1 19 20 package org.netbeans.modules.java.editor.overridden; 21 22 import com.sun.source.tree.CompilationUnitTree; 23 import com.sun.source.tree.Tree; 24 import java.io.IOException ; 25 import java.net.URL ; 26 import java.util.ArrayList ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.HashMap ; 30 import java.util.HashSet ; 31 import java.util.EnumSet ; 32 import java.util.LinkedList ; 33 import java.util.List ; 34 import java.util.Map ; 35 import java.util.Set ; 36 import java.util.logging.Level ; 37 import java.util.logging.Logger ; 38 import javax.lang.model.element.Element; 39 import javax.lang.model.element.ExecutableElement; 40 import javax.lang.model.element.Modifier; 41 import javax.lang.model.element.Name; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.type.DeclaredType; 44 import javax.lang.model.type.TypeKind; 45 import javax.lang.model.type.TypeMirror; 46 import javax.lang.model.util.ElementFilter; 47 import javax.swing.text.BadLocationException ; 48 import javax.swing.text.Position ; 49 import javax.swing.text.StyledDocument ; 50 import org.netbeans.api.java.classpath.ClassPath; 51 import org.netbeans.api.java.classpath.ClassPath; 52 import org.netbeans.api.java.classpath.ClassPath.Entry; 53 import org.netbeans.api.java.queries.SourceForBinaryQuery; 54 import org.netbeans.api.java.source.CancellableTask; 55 import org.netbeans.api.java.source.ClassIndex.SearchKind; 56 import org.netbeans.api.java.source.ClasspathInfo; 57 import org.netbeans.api.java.source.CompilationController; 58 import org.netbeans.api.java.source.CompilationInfo; 59 import org.netbeans.api.java.source.ElementHandle; 60 import org.netbeans.api.java.source.JavaSource; 61 import org.netbeans.api.java.source.SourceUtils; 62 import org.netbeans.api.java.source.ClassIndex; 63 import org.netbeans.api.timers.TimesCollector; 64 import org.netbeans.spi.java.classpath.support.ClassPathSupport; 65 import org.openide.ErrorManager; 66 import org.openide.cookies.EditorCookie; 67 import org.openide.filesystems.FileObject; 68 import org.openide.filesystems.FileUtil; 69 import org.openide.loaders.DataObject; 70 import org.openide.text.NbDocument; 71 import org.openide.util.RequestProcessor; 72 import org.openide.util.TopologicalSortException; 73 import org.openide.util.Utilities; 74 75 76 80 public class IsOverriddenAnnotationHandler implements CancellableTask<CompilationInfo> { 81 82 private static final boolean enableReverseLookups = Boolean.getBoolean("org.netbeans.java.editor.enableReverseLookups"); 83 static final Logger LOG = Logger.getLogger(IsOverriddenAnnotationHandler.class.getName()); 84 85 private FileObject file; 86 87 IsOverriddenAnnotationHandler(FileObject file) { 88 this.file = file; 89 90 TimesCollector.getDefault().reportReference(file, IsOverriddenAnnotationHandler.class.getName(), "[M] IsOverriddenAnnotationHandler", this); 91 } 92 93 public StyledDocument getDocument() { 94 try { 95 DataObject d = DataObject.find(file); 96 EditorCookie ec = d.getCookie(EditorCookie.class); 97 98 if (ec == null) 99 return null; 100 101 return ec.getDocument(); 102 } catch (IOException e) { 103 LOG.log(Level.INFO, "Cannot find DataObject for file: " + FileUtil.getFileDisplayName(file), e); 104 return null; 105 } 106 } 107 108 private IsOverriddenVisitor visitor; 109 110 public void run(CompilationInfo info) { 111 resume(); 112 113 StyledDocument doc = getDocument(); 114 115 if (doc == null) { 116 LOG.log(Level.INFO, "Cannot get document!"); 117 return ; 118 } 119 120 long startTime = System.currentTimeMillis(); 121 122 try { 123 List <IsOverriddenAnnotation> annotations = process(info, doc); 124 125 if (annotations == null) { 126 return ; 128 } 129 130 newAnnotations(annotations); 131 } finally { 132 synchronized (this) { 133 visitor = null; 134 } 135 136 TimesCollector.getDefault().reportTime(file, "is-overridden", "Overridden in", System.currentTimeMillis() - startTime); 137 } 138 } 139 140 private FileObject findSourceRoot() { 141 final ClassPath cp = ClassPath.getClassPath(file, ClassPath.SOURCE); 142 if (cp != null) { 143 for (FileObject root : cp.getRoots()) { 144 if (FileUtil.isParentOf(root, file)) 145 return root; 146 } 147 } 148 return null; 150 } 151 152 private synchronized Set <FileObject> findReverseSourceRoots(final FileObject thisSourceRoot, final FileObject thisFile) { 154 final Object o = new Object (); 155 final Set <FileObject> reverseSourceRoots = new HashSet <FileObject>(); 156 157 RequestProcessor.getDefault().post(new Runnable () { 158 public void run() { 159 long startTime = System.currentTimeMillis(); 160 Set <FileObject> reverseSourceRootsInt = new HashSet <FileObject>(ReverseSourceRootsLookup.reverseSourceRootsLookup(thisSourceRoot)); 161 long endTime = System.currentTimeMillis(); 162 163 TimesCollector.getDefault().reportTime(thisFile, "findReverseSourceRoots", "Find Reverse Source Roots", endTime - startTime); 164 165 synchronized (o) { 166 reverseSourceRoots.addAll(reverseSourceRootsInt); 167 } 168 169 wakeUp(); 170 } 171 }); 172 173 try { 174 wait(); 175 } catch (InterruptedException ex) { 176 ErrorManager.getDefault().notify(ex); 177 } 178 179 return reverseSourceRoots; 180 } 181 182 public static AnnotationType detectOverrides(CompilationInfo info, TypeElement type, ExecutableElement ee, List <ElementDescription> result) { 183 final Map <Name, List <ExecutableElement>> name2Method = new HashMap <Name, List <ExecutableElement>>(); 184 185 sortOutMethods(info, name2Method, type, false); 186 187 List <ExecutableElement> lee = name2Method.get(ee.getSimpleName()); 188 189 if (lee == null || lee.isEmpty()) { 190 return null; 191 } 192 193 Set <ExecutableElement> seenMethods = new HashSet <ExecutableElement>(); 194 195 for (ExecutableElement overridee : lee) { 196 if (info.getElements().overrides(ee, overridee, SourceUtils.getEnclosingTypeElement(ee))) { 197 if (seenMethods.add(overridee)) { 198 result.add(new ElementDescription(info, overridee)); 199 } 200 } 201 } 202 203 if (!result.isEmpty()) { 204 for (ElementDescription ed : result) { 205 if (!ed.getModifiers().contains(Modifier.ABSTRACT)) { 206 return AnnotationType.OVERRIDES; 207 } 208 } 209 210 return AnnotationType.IMPLEMENTS; 211 } 212 213 return null; 214 } 215 216 List <IsOverriddenAnnotation> process(CompilationInfo info, final StyledDocument doc) { 217 IsOverriddenVisitor v; 218 219 synchronized (this) { 220 if (isCanceled()) 221 return null; 222 223 v = visitor = new IsOverriddenVisitor(doc, info); 224 } 225 226 CompilationUnitTree unit = info.getCompilationUnit(); 227 228 long startTime1 = System.currentTimeMillis(); 229 230 v.scan(unit, null); 231 232 long endTime1 = System.currentTimeMillis(); 233 234 TimesCollector.getDefault().reportTime(file, "overridden-scanner", "Overridden Scanner", endTime1 - startTime1); 235 236 Set <FileObject> reverseSourceRoots; 237 238 if (enableReverseLookups) { 239 FileObject thisSourceRoot = findSourceRoot(); 240 if (thisSourceRoot == null) { 241 return null; 242 } 243 244 reverseSourceRoots = findReverseSourceRoots(thisSourceRoot, info.getFileObject()); 245 246 reverseSourceRoots.add(thisSourceRoot); 248 } else { 249 reverseSourceRoots = null; 250 } 251 252 LOG.log(Level.FINE, "reverseSourceRoots: {0}", reverseSourceRoots); 253 254 List <IsOverriddenAnnotation> annotations = new ArrayList <IsOverriddenAnnotation>(); 255 256 for (ElementHandle<TypeElement> td : v.type2Declaration.keySet()) { 257 if (isCanceled()) 258 return null; 259 260 LOG.log(Level.FINE, "type: {0}", td.getQualifiedName()); 261 262 final Map <Name, List <ExecutableElement>> name2Method = new HashMap <Name, List <ExecutableElement>>(); 263 264 TypeElement resolvedType = td.resolve(info); 265 266 if (resolvedType == null) 267 continue; 268 269 sortOutMethods(info, name2Method, resolvedType, false); 270 271 for (ElementHandle<ExecutableElement> methodHandle : v.type2Declaration.get(td)) { 272 if (isCanceled()) 273 return null; 274 275 ExecutableElement ee = methodHandle.resolve(info); 276 277 if (ee == null) 278 continue; 279 280 if (LOG.isLoggable(Level.FINE)) { 281 LOG.log(Level.FINE, "method: {0}", ee.toString()); 282 } 283 284 List <ExecutableElement> lee = name2Method.get(ee.getSimpleName()); 285 286 if (lee == null || lee.isEmpty()) { 287 continue; 288 } 289 290 Set <ExecutableElement> seenMethods = new HashSet <ExecutableElement>(); 291 List <ElementDescription> overrides = new ArrayList <ElementDescription>(); 292 293 for (ExecutableElement overridee : lee) { 294 if (info.getElements().overrides(ee, overridee, SourceUtils.getEnclosingTypeElement(ee))) { 295 if (seenMethods.add(overridee)) { 296 overrides.add(new ElementDescription(info, overridee)); 297 } 298 } 299 } 300 301 if (!overrides.isEmpty()) { 302 int position = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), v.declaration2Tree.get(methodHandle)); 303 Position pos = getPosition(doc, position); 304 305 if (pos == null) { 306 continue; 308 } 309 310 StringBuffer tooltip = new StringBuffer (); 311 boolean wasOverrides = false; 312 313 boolean newline = false; 314 315 for (ElementDescription ed : overrides) { 316 if (newline) { 317 tooltip.append("\n"); 318 } 319 320 newline = true; 321 322 if (ed.getModifiers().contains(Modifier.ABSTRACT)) { 323 tooltip.append("Implements: " + ed.getDisplayName()); 324 } else { 325 tooltip.append("Overrides: " + ed.getDisplayName()); 326 wasOverrides = true; 327 } 328 } 329 330 annotations.add(new IsOverriddenAnnotation(doc, pos, wasOverrides ? AnnotationType.OVERRIDES : AnnotationType.IMPLEMENTS, tooltip.toString(), overrides)); 331 } 332 } 333 334 if (enableReverseLookups) { 335 String typeOverridden = null; 336 AnnotationType typeType = null; 337 TypeElement resolved = td.resolve(info); 338 339 340 if (resolved == null) { 341 Logger.getLogger("global").log(Level.SEVERE, "IsOverriddenAnnotationHandler: resolved == null!"); 342 continue; 343 } 344 345 if (resolved.getKind().isInterface()) { 346 typeOverridden = "Has Implementations"; 347 typeType = AnnotationType.HAS_IMPLEMENTATION; 348 } 349 350 if (resolved.getKind().isClass()) { 351 typeOverridden = "Is Overridden:"; 352 typeType = AnnotationType.IS_OVERRIDDEN; 353 } 354 355 final Map <ElementHandle<ExecutableElement>, List <ElementDescription>> overriding = new HashMap <ElementHandle<ExecutableElement>, List <ElementDescription>>(); 356 final List <ElementDescription> overridingClasses = new ArrayList <ElementDescription>(); 357 358 long startTime = System.currentTimeMillis(); 359 long[] classIndexTime = new long[1]; 360 final Map <FileObject, Set <ElementHandle<TypeElement>>> users = computeUsers(reverseSourceRoots, ElementHandle.create(resolved), classIndexTime); 361 long endTime = System.currentTimeMillis(); 362 363 if (users == null) { 364 return null; 365 } 366 367 TimesCollector.getDefault().reportTime(file, "overridden-users-classindex", "Overridden Users Class Index", classIndexTime[0]); 368 TimesCollector.getDefault().reportTime(file, "overridden-users", "Overridden Users", endTime - startTime); 369 370 for (Map.Entry <FileObject, Set <ElementHandle<TypeElement>>> data : users.entrySet()) { 371 if (isCanceled()) 372 return null; 373 374 findOverriddenAnnotations(data.getKey(), data.getValue(), td, v.type2Declaration.get(td), overriding, overridingClasses); 375 } 376 377 if (!overridingClasses.isEmpty()) { 378 Tree t = v.declaration2Class.get(td); 379 380 if (t != null) { 381 Position pos = getPosition(doc, (int) info.getTrees().getSourcePositions().getStartPosition(unit, t)); 382 383 if (pos == null) { 384 continue; 386 } 387 388 annotations.add(new IsOverriddenAnnotation(doc, pos, typeType, typeOverridden.toString(), overridingClasses)); 389 } 390 } 391 392 for (ElementHandle<ExecutableElement> original : overriding.keySet()) { 393 if (isCanceled()) 394 return null; 395 396 Position pos = getPosition(doc, (int) info.getTrees().getSourcePositions().getStartPosition(unit, v.declaration2Tree.get(original))); 397 398 if (pos == null) { 399 continue; 401 } 402 403 Set <Modifier> mods = original.resolve(info).getModifiers(); 404 String tooltip = null; 405 406 if (mods.contains(Modifier.ABSTRACT)) { 407 tooltip = "Has Implementations"; 408 } else { 409 tooltip = "Is Overridden"; 410 } 411 412 IsOverriddenAnnotation ann = new IsOverriddenAnnotation(doc, pos, mods.contains(Modifier.ABSTRACT) ? AnnotationType.HAS_IMPLEMENTATION : AnnotationType.IS_OVERRIDDEN, tooltip, overriding.get(original)); 413 414 annotations.add(ann); 415 } 416 } 417 } 418 419 if (isCanceled()) 420 return null; 421 else 422 return annotations; 423 } 424 425 private static final ClassPath EMPTY = ClassPathSupport.createClassPath(new URL [0]); 426 427 private Set <ElementHandle<TypeElement>> computeUsers(FileObject source, Set <ElementHandle<TypeElement>> base, long[] classIndexCumulative) { 428 ClasspathInfo cpinfo = ClasspathInfo.create(EMPTY, EMPTY, ClassPathSupport.createClassPath(new FileObject[] {source})); 429 430 long startTime = System.currentTimeMillis(); 431 432 try { 433 List <ElementHandle<TypeElement>> l = new LinkedList <ElementHandle<TypeElement>>(base); 434 Set <ElementHandle<TypeElement>> result = new HashSet <ElementHandle<TypeElement>>(); 435 436 while (!l.isEmpty()) { 437 ElementHandle<TypeElement> eh = l.remove(0); 438 439 result.add(eh); 440 441 l.addAll(cpinfo.getClassIndex().getElements(eh, Collections.singleton(SearchKind.IMPLEMENTORS), EnumSet.of(ClassIndex.SearchScope.SOURCE))); 442 } 443 return result; 444 } finally { 445 classIndexCumulative[0] += (System.currentTimeMillis() - startTime); 446 } 447 } 448 449 private Map <FileObject, Set <ElementHandle<TypeElement>>> computeUsers(Set <FileObject> sources, ElementHandle<TypeElement> base, long[] classIndexCumulative) { 450 Map <FileObject, Collection <FileObject>> edges = new HashMap <FileObject, Collection <FileObject>>(); 451 Map <FileObject, Collection <FileObject>> dependsOn = new HashMap <FileObject, Collection <FileObject>>(); 452 453 for (FileObject source : sources) { 454 edges.put(source, new ArrayList <FileObject>()); 455 } 456 457 for (FileObject source : sources) { 458 List <FileObject> deps = new ArrayList <FileObject>(); 459 460 dependsOn.put(source, deps); 461 462 for (Entry entry : ClassPath.getClassPath(source, ClassPath.COMPILE).entries()) { for (FileObject s : SourceForBinaryQuery.findSourceRoots(entry.getURL()).getRoots()) { 464 Collection <FileObject> targets = edges.get(s); 465 466 if (targets != null) { 467 targets.add(source); 468 } 469 470 deps.add(s); 471 } 472 } 473 } 474 475 List <FileObject> sourceRoots = new ArrayList <FileObject>(sources); 476 477 try { 478 Utilities.topologicalSort(sourceRoots, edges); 479 } catch (TopologicalSortException ex) { 480 LOG.log(Level.WARNING, "internal error", ex); 481 return null; 482 } 483 484 Map <FileObject, Set <ElementHandle<TypeElement>>> result = new HashMap <FileObject, Set <ElementHandle<TypeElement>>>(); 485 486 for (FileObject file : sourceRoots) { 487 Set <ElementHandle<TypeElement>> baseTypes = new HashSet <ElementHandle<TypeElement>>(); 488 489 baseTypes.add(base); 490 491 for (FileObject dep : dependsOn.get(file)) { 492 Set <ElementHandle<TypeElement>> depTypes = result.get(dep); 493 494 if (depTypes != null) { 495 baseTypes.addAll(depTypes); 496 } 497 } 498 499 Set <ElementHandle<TypeElement>> types = computeUsers(file, baseTypes, classIndexCumulative); 500 501 types.removeAll(baseTypes); 502 503 result.put(file, types); 504 } 505 506 return result; 507 } 508 private void findOverriddenAnnotations( 509 FileObject sourceRoot, 510 final Set <ElementHandle<TypeElement>> users, 511 final ElementHandle<TypeElement> originalType, 512 final List <ElementHandle<ExecutableElement>> methods, 513 final Map <ElementHandle<ExecutableElement>, List <ElementDescription>> overriding, 514 final List <ElementDescription> overridingClasses) { 515 ClasspathInfo cpinfo = ClasspathInfo.create(sourceRoot); 516 517 if (!users.isEmpty()) { 518 JavaSource js = JavaSource.create(cpinfo); 519 520 try { 521 js.runUserActionTask(new CancellableTask<CompilationController>() { 522 public void cancel() { 523 cancel(); 524 } 525 public void run(CompilationController controller) throws Exception { 526 Set <Element> seenElements = new HashSet <Element>(); 527 528 for (ElementHandle<TypeElement> typeHandle : users) { 529 if (isCanceled()) 530 return; 531 TypeElement type = typeHandle.resolve(controller); 532 Element resolvedOriginalType = originalType.resolve(controller); 533 534 if (!seenElements.add(resolvedOriginalType)) 535 continue; 536 537 if (controller.getTypes().isSubtype(type.asType(), resolvedOriginalType.asType())) { 538 overridingClasses.add(new ElementDescription(controller, type)); 539 540 for (ElementHandle<ExecutableElement> originalMethodHandle : methods) { 541 ExecutableElement originalMethod = originalMethodHandle.resolve(controller); 542 543 if (originalMethod != null) { 544 ExecutableElement overrider = getImplementationOf(controller, originalMethod, type); 545 546 if (overrider == null) 547 continue; 548 549 List <ElementDescription> overriddingMethods = overriding.get(originalMethodHandle); 550 551 if (overriddingMethods == null) { 552 overriding.put(originalMethodHandle, overriddingMethods = new ArrayList <ElementDescription>()); 553 } 554 555 overriddingMethods.add(new ElementDescription(controller, overrider)); 556 } else { 557 Logger.getLogger("global").log(Level.SEVERE, "IsOverriddenAnnotationHandler: originalMethod == null!"); 558 } 559 } 560 } 561 } 562 } 563 },true); 564 } catch (Exception e) { 565 ErrorManager.getDefault().notify(e); 566 } 567 } 568 } 569 570 private ExecutableElement getImplementationOf(CompilationInfo info, ExecutableElement overridee, TypeElement implementor) { 571 for (ExecutableElement overrider : ElementFilter.methodsIn(implementor.getEnclosedElements())) { 572 if (info.getElements().overrides(overrider, overridee, implementor)) { 573 return overrider; 574 } 575 } 576 577 return null; 578 } 579 580 private boolean canceled; 581 582 public synchronized void cancel() { 583 canceled = true; 584 585 if (visitor != null) { 586 visitor.cancel(); 587 } 588 589 wakeUp(); 590 } 591 592 private synchronized void resume() { 593 canceled = false; 594 } 595 596 private synchronized void wakeUp() { 597 notifyAll(); 598 } 599 600 private synchronized boolean isCanceled() { 601 return canceled; 602 } 603 604 private void newAnnotations(List <IsOverriddenAnnotation> as) { 605 AnnotationsHolder a = AnnotationsHolder.get(file); 606 607 if (a != null) { 608 a.setNewAnnotations(as); 609 } 610 } 611 612 private static void sortOutMethods(CompilationInfo info, Map <Name, List <ExecutableElement>> where, Element td, boolean current) { 613 if (current) { 614 Map <Name, List <ExecutableElement>> newlyAdded = new HashMap <Name, List <ExecutableElement>>(); 615 616 OUTTER: for (ExecutableElement ee : ElementFilter.methodsIn(td.getEnclosedElements())) { 617 Name name = ee.getSimpleName(); 618 List <ExecutableElement> alreadySeen = where.get(name); 619 620 if (alreadySeen != null) { 621 for (ExecutableElement seen : alreadySeen) { 622 if (info.getElements().overrides(seen, ee, (TypeElement) seen.getEnclosingElement())) { 623 continue OUTTER; } 625 } 626 } 627 628 List <ExecutableElement> lee = newlyAdded.get(name); 629 630 if (lee == null) { 631 newlyAdded.put(name, lee = new ArrayList <ExecutableElement>()); 632 } 633 634 lee.add(ee); 635 } 636 637 for (Map.Entry <Name, List <ExecutableElement>> e : newlyAdded.entrySet()) { 638 List <ExecutableElement> lee = where.get(e.getKey()); 639 640 if (lee == null) { 641 where.put(e.getKey(), e.getValue()); 642 } else { 643 lee.addAll(e.getValue()); 644 } 645 } 646 } 647 648 for (TypeMirror superType : info.getTypes().directSupertypes(td.asType())) { 649 if (superType.getKind() == TypeKind.DECLARED) { 650 sortOutMethods(info, where, ((DeclaredType) superType).asElement(), true); 651 } 652 } 653 } 654 655 private static Position getPosition(final StyledDocument doc, final int offset) { 656 class Impl implements Runnable { 657 private Position pos; 658 public void run() { 659 if (offset < 0 || offset >= doc.getLength()) 660 return ; 661 662 try { 663 pos = doc.createPosition(offset - NbDocument.findLineColumn(doc, offset)); 664 } catch (BadLocationException ex) { 665 LOG.log(Level.FINE, null, ex); 667 } 668 } 669 } 670 671 Impl i = new Impl(); 672 673 doc.render(i); 674 675 return i.pos; 676 } 677 678 } 679 | Popular Tags |