1 19 20 package org.netbeans.modules.java.source.usages; 21 22 import com.sun.source.tree.ClassTree; 23 import com.sun.source.tree.CompilationUnitTree; 24 import com.sun.source.tree.ErroneousTree; 25 import com.sun.source.tree.IdentifierTree; 26 import com.sun.source.tree.MemberSelectTree; 27 import com.sun.source.tree.MethodTree; 28 import com.sun.source.tree.NewClassTree; 29 import com.sun.source.tree.ParameterizedTypeTree; 30 import com.sun.source.tree.Tree; 31 import com.sun.source.util.TreeScanner; 32 import com.sun.tools.javac.api.JavacTaskImpl; 33 import com.sun.tools.javac.code.Symbol; 34 import com.sun.tools.javac.code.Symbol; 35 import com.sun.tools.javac.code.Symbol.ClassSymbol; 36 import com.sun.tools.javac.code.Types; 37 import com.sun.tools.javac.tree.JCTree; 38 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 39 import com.sun.tools.javac.util.Name; 40 import com.sun.tools.javac.util.Name; 41 import java.io.BufferedReader ; 42 import java.io.IOException ; 43 import java.io.OutputStreamWriter ; 44 import java.io.PrintWriter ; 45 import java.io.PrintWriter ; 46 import java.net.MalformedURLException ; 47 import java.net.URI ; 48 import java.util.ArrayList ; 49 import java.util.EnumSet ; 50 import java.util.HashMap ; 51 import java.util.HashSet ; 52 import java.util.LinkedList ; 53 import java.util.List ; 54 import java.util.Map ; 55 import java.util.Set ; 56 import java.util.Stack ; 57 import java.util.logging.Logger ; 58 import javax.lang.model.element.Element; 59 import javax.lang.model.element.ElementKind; 60 import javax.lang.model.element.TypeElement; 61 import javax.lang.model.type.ArrayType; 62 import javax.lang.model.type.DeclaredType; 63 import javax.lang.model.type.TypeKind; 64 import javax.lang.model.type.TypeMirror; 65 import javax.tools.JavaFileManager; 66 import javax.tools.JavaFileObject; 67 import javax.tools.StandardLocation; 68 import org.netbeans.api.java.classpath.ClassPath; 69 import org.netbeans.modules.java.source.parsing.FileObjects; 70 import org.openide.filesystems.FileObject; 71 import org.openide.filesystems.URLMapper; 72 import org.openide.util.Exceptions; 73 74 78 public class SourceAnalyser { 79 80 private final Index index; 81 private final Map <String , List <String >> references; 82 private final Set <String > toDelete; 83 84 85 public SourceAnalyser (final Index index) { 86 assert index != null; 87 this.index = index; 88 this.references = new HashMap <String , List <String >> (); 89 this.toDelete = new HashSet <String > (); 90 } 91 92 93 public void store () throws IOException { 94 if (this.references.size() > 0 || this.toDelete.size() > 0) { 95 this.index.store(this.references, toDelete); 96 this.references.clear(); 97 this.toDelete.clear(); 98 } 99 } 100 101 public boolean isValid () throws IOException { 102 return this.index.isValid(true); 103 } 104 105 public void analyse (final Iterable <? extends CompilationUnitTree> data, JavacTaskImpl jt, JavaFileManager manager, javax.tools.JavaFileObject sibling) throws IOException { 106 final Map <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> usages = new HashMap <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> (); 107 for (CompilationUnitTree cu : data) { 108 UsagesVisitor uv = new UsagesVisitor (jt, cu, manager, sibling); 109 uv.scan(cu,usages); 110 if (uv.rsList != null && uv.rsList.size()>0) { 111 final int index = uv.sourceName.lastIndexOf('.'); final String pkg = index == -1 ? "" : uv.sourceName.substring(0,index); final String rsName = (index == -1 ? uv.sourceName : uv.sourceName.substring(index+1)) + '.' + FileObjects.RS; javax.tools.FileObject fo = manager.getFileForOutput(StandardLocation.CLASS_OUTPUT, pkg, rsName, sibling); 115 assert fo != null; 116 BufferedReader in = new BufferedReader (fo.openReader(false)); 117 try { 118 String line; 119 while ((line = in.readLine())!=null) { 120 uv.rsList.add (line); 121 } 122 } finally { 123 in.close(); 124 } 125 PrintWriter rsOut = new PrintWriter (fo.openWriter()); 126 try { 127 for (String sig : uv.rsList) { 128 rsOut.println(sig); 129 } 130 } finally { 131 rsOut.close(); 132 } 133 } 134 } 135 for (Map.Entry <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> oe : usages.entrySet()) { 136 List <String > ru = getClassReferences (oe.getKey()); 137 Map <String ,Set <ClassIndexImpl.UsageType>> oeValue = oe.getValue(); 138 for (Map.Entry <String ,Set <ClassIndexImpl.UsageType>> ue : oeValue.entrySet()) { 139 ru.add (DocumentUtil.encodeUsage(ue.getKey(),ue.getValue())); 140 } 141 } 142 } 143 144 void analyseUnitAndStore (final CompilationUnitTree cu, final JavacTaskImpl jt) throws IOException { 145 try { 146 final Map <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> usages = new HashMap <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> (); 147 List <String > topLevels = new ArrayList <String >(); 148 UsagesVisitor uv = new UsagesVisitor (jt, cu, topLevels); 149 uv.scan(cu,usages); 150 for (Map.Entry <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> oe : usages.entrySet()) { 151 String className = oe.getKey(); 152 List <String > ru = getClassReferences (className); 153 Map <String ,Set <ClassIndexImpl.UsageType>> oeValue = oe.getValue(); 154 for (Map.Entry <String ,Set <ClassIndexImpl.UsageType>> ue : oeValue.entrySet()) { 155 ru.add (DocumentUtil.encodeUsage(ue.getKey(),ue.getValue())); 156 } 157 } 158 this.index.store(this.references, topLevels); 159 } finally { 160 this.references.clear(); 161 } 162 } 163 164 public void delete (final String className) throws IOException { 165 if (!this.index.isValid(false)) { 166 return; 167 } 168 this.toDelete.add(className); 169 } 170 171 172 private List <String > getClassReferences (final String className) { 173 assert className != null; 174 List <String > result = this.references.get (className); 175 if (result == null) { 176 result = new LinkedList <String >(); 177 this.references.put(className,result); 178 } 179 return result; 180 } 181 182 183 private static void dumpUsages(final Map <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> usages) throws IOException { 184 assert usages != null; 185 for (Map.Entry <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> oe : usages.entrySet()) { 186 System.out.println("Usages in class: " + oe.getKey()); for (Map.Entry <String ,Set <ClassIndexImpl.UsageType>> ue : oe.getValue().entrySet()) { 188 System.out.println("\t"+ue.getKey()+"\t: "+ue.getValue().toString()); } 190 } 191 } 192 193 static class UsagesVisitor extends TreeScanner<Void ,Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>>> { 194 195 enum State {EXTENDS, IMPLEMENTS, GT, OTHER}; 196 197 private final Stack <String > activeClass; 198 private JavaFileManager manager; 199 private final JavacTaskImpl jt; 200 private final Name errorName; 201 private final CompilationUnitTree cu; 202 private final Types types; 203 private final javax.tools.JavaFileObject sibling; 204 private final String sourceName; 205 private final boolean signatureFiles; 206 private State state; 207 private Element enclosingElement = null; 208 private Set <String > rsList; 209 private List <? super String > topLevels; 210 211 212 public UsagesVisitor (JavacTaskImpl jt, CompilationUnitTree cu, JavaFileManager manager, javax.tools.JavaFileObject sibling) { 213 assert jt != null; 214 assert cu != null; 215 assert manager != null; 216 assert sibling != null; 217 this.activeClass = new Stack <String > (); 218 this.jt = jt; 219 this.errorName = Name.Table.instance(jt.getContext()).error; 220 this.state = State.OTHER; 221 this.types = com.sun.tools.javac.code.Types.instance(jt.getContext()); 222 this.cu = cu; 223 this.signatureFiles = true; 224 this.manager = manager; 225 this.sibling = sibling; 226 this.sourceName = this.manager.inferBinaryName(StandardLocation.SOURCE_PATH, this.sibling); 227 } 228 229 protected UsagesVisitor (JavacTaskImpl jt, CompilationUnitTree cu, List <? super String > topLevels) { 230 assert jt != null; 231 assert cu != null; 232 233 this.activeClass = new Stack <String > (); 234 this.jt = jt; 235 this.errorName = Name.Table.instance(jt.getContext()).error; 236 this.state = State.OTHER; 237 this.types = com.sun.tools.javac.code.Types.instance(jt.getContext()); 238 this.cu = cu; 239 this.signatureFiles = false; 240 this.manager = null; 241 this.sibling = null; 242 this.sourceName = ""; this.topLevels = topLevels; 244 } 245 246 final Types getTypes() { 247 return types; 248 } 249 250 public @Override Void scan(Tree node, Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 251 if (node == null) { 252 return null; 253 } 254 super.scan (node,p); 255 return null; 256 } 257 258 public @Override Void visitMemberSelect(final MemberSelectTree node, final Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 259 handleVisitIdentSelect (((JCTree.JCFieldAccess)node).sym, p); 260 State oldState = this.state; 261 this.state = State.OTHER; 262 Void ret = super.visitMemberSelect (node, p); 263 this.state = oldState; 264 return ret; 265 } 266 267 public @Override Void visitIdentifier(final IdentifierTree node, final Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 268 handleVisitIdentSelect (((JCTree.JCIdent)node).sym, p); 269 return super.visitIdentifier(node, p); 270 } 271 272 private void handleVisitIdentSelect (Symbol sym, final Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 273 if (!activeClass.empty()) { 274 if (sym != null) { 275 if (sym.getKind().isClass() || sym.getKind().isInterface()) { 276 final String className = encodeClassName(sym); 277 if (className != null) { 278 switch (this.state) { 279 case EXTENDS: 280 addUsage(activeClass.peek(),className, p, ClassIndexImpl.UsageType.SUPER_CLASS); 281 break; 282 case IMPLEMENTS: 283 addUsage (activeClass.peek(),className,p, ClassIndexImpl.UsageType.SUPER_INTERFACE); 284 break; 285 case OTHER: 286 case GT: 287 addUsage (activeClass.peek(),className,p, ClassIndexImpl.UsageType.TYPE_REFERENCE); 288 break; 289 } 290 } 291 } 292 else if (sym.getKind().isField()) { 293 final Symbol owner = sym.getEnclosingElement(); 294 final String className = encodeClassName(owner); 295 if (className != null) { 296 addUsage (activeClass.peek(),className,p,ClassIndexImpl.UsageType.FIELD_REFERENCE); 297 } 298 } 299 else if (sym.getKind() == ElementKind.CONSTRUCTOR || sym.getKind() == ElementKind.METHOD) { 300 final Symbol owner = sym.getEnclosingElement(); 301 final String className = encodeClassName(owner); 302 if (className != null) { 303 addUsage (activeClass.peek(),className,p,ClassIndexImpl.UsageType.METHOD_REFERENCE); 304 } 305 } 306 } 307 } 308 } 309 310 public @Override Void visitParameterizedType(ParameterizedTypeTree node, final Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 311 scan(node.getType(), p); 312 State currState = this.state; 313 this.state = State.GT; 314 scan(node.getTypeArguments(), p); 315 this.state = currState; 316 return null; 317 } 318 319 void dump(TypeElement clazz, String className, Element enclosingElement) { 320 PrintWriter output = null; 321 if (this.rsList != null) { 322 this.rsList.add (className); 323 } 324 try { 325 JavaFileObject jfo = manager.getJavaFileForOutput(StandardLocation.CLASS_OUTPUT, className, JavaFileObject.Kind.CLASS, sibling); 326 327 output = new PrintWriter (new OutputStreamWriter (jfo.openOutputStream(), "UTF-8")); 328 329 SymbolDumper.dump(output, types, clazz, enclosingElement); 330 331 output.close(); 332 333 } catch (IOException e) { 334 Exceptions.printStackTrace(e); 335 } finally { 336 if (output != null) { 337 output.close(); 338 } 339 } 340 } 341 342 protected boolean shouldGenerate (final String binaryName, ClassSymbol sym) { 343 if (!signatureFiles || binaryName == null) { 344 return false; 345 } 346 if (sym.getQualifiedName().isEmpty()) { 347 return true; 348 } 349 Symbol enclosing = sym.getEnclosingElement(); 350 while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { 351 if (!enclosing.getKind().isClass() && !enclosing.getKind().isInterface()) { 352 return true; 353 } 354 enclosing = enclosing.getEnclosingElement(); 355 } 356 return false; 357 } 358 359 public @Override Void visitClass (final ClassTree node, final Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 360 final ClassSymbol sym = ((JCTree.JCClassDecl)node).sym; 361 boolean errorInDecl = false; 362 boolean errorIgnorSubtree = true; 363 String className = null; 364 if (sym != null) { 365 errorInDecl = hasErrorName(sym); 366 if (errorInDecl) { 367 if (activeClass.size()>0) { 368 activeClass.push (activeClass.get(0)); 369 errorIgnorSubtree = false; 370 } 371 else { 372 if (this.cu instanceof JCTree.JCCompilationUnit) { 373 JavaFileObject jfo = ((JCTree.JCCompilationUnit)this.cu).sourcefile; 374 if (jfo != null) { 375 URI uri = jfo.toUri(); 376 if (uri != null && uri.isAbsolute()) { 377 try { 378 FileObject fo = URLMapper.findFileObject(uri.toURL()); 379 if (fo != null) { 380 ClassPath cp = ClassPath.getClassPath(fo,ClassPath.SOURCE); 381 if (cp != null) { 382 className = cp.getResourceName(fo,'.',false); 383 } 384 } 385 } catch (MalformedURLException e) { 386 Exceptions.printStackTrace(e); 387 } 388 } 389 } 390 } 391 if (className != null) { 392 final String classNameType = className + DocumentUtil.encodeKind(ElementKind.CLASS); 393 if (topLevels != null && activeClass.isEmpty()) { 394 topLevels.add (className); 395 } 396 activeClass.push (classNameType); 397 errorIgnorSubtree = false; 398 addUsage (classNameType,className, p, ClassIndexImpl.UsageType.TYPE_REFERENCE); 399 } 400 else { 401 Logger.getLogger("global").warning(String.format("Cannot resolve %s, ignoring whole subtree.\n",sym.toString())); } 403 } 404 } 405 else { 406 407 final StringBuilder classNameBuilder = new StringBuilder (); 408 ClassFileUtil.encodeClassName(sym, classNameBuilder, '.'); className = classNameBuilder.toString(); 410 classNameBuilder.append(DocumentUtil.encodeKind(sym.getKind())); 411 final String classNameType = classNameBuilder.toString(); 412 if (signatureFiles && activeClass.isEmpty() && !className.equals(sourceName)) { 413 rsList = new HashSet <String >(); 414 } 415 if (topLevels != null && activeClass.isEmpty()) { 416 topLevels.add (className); 417 } 418 activeClass.push (classNameType); 419 errorIgnorSubtree = false; 420 addUsage (classNameType,className, p, ClassIndexImpl.UsageType.TYPE_REFERENCE); 421 } 422 423 } 424 if (!errorIgnorSubtree) { 425 Element old = enclosingElement; 426 try { 427 enclosingElement = sym; 428 scan(node.getModifiers(), p); 429 scan(node.getTypeParameters(), p); 430 state = errorInDecl ? State.OTHER : State.EXTENDS; 431 scan(node.getExtendsClause(), p); 432 state = errorInDecl ? State.OTHER : State.IMPLEMENTS; 433 scan(node.getImplementsClause(), p); 434 state = State.OTHER; 435 scan(node.getMembers(), p); 436 activeClass.pop(); 437 } finally { 438 enclosingElement = old; 439 } 440 } 441 if (!errorInDecl && shouldGenerate(className, sym)) { 442 dump(sym, className, enclosingElement); 443 } 444 return null; 445 } 446 447 public @Override Void visitNewClass(NewClassTree node, Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 448 final Symbol sym = ((JCTree.JCNewClass)node).constructor; 449 if (sym != null) { 450 final Symbol owner = sym.getEnclosingElement(); 451 if (owner != null && owner.getKind().isClass()) { 452 final String className = encodeClassName(owner); 453 if (className != null) { 454 addUsage(activeClass.peek(),className,p,ClassIndexImpl.UsageType.METHOD_REFERENCE); 455 } 456 } 457 } 458 return super.visitNewClass (node,p); 459 } 460 461 public @Override Void visitErroneous(final ErroneousTree tree, Map <String ,Map <String , Set <ClassIndexImpl.UsageType>>> p) { 462 List <? extends Tree> trees = tree.getErrorTrees(); 463 for (Tree t : trees) { 464 this.scan(t,p); 465 } 466 return null; 467 } 468 469 public Void visitMethod(MethodTree node, Map <String , Map <String , Set <ClassIndexImpl.UsageType>>> p) { 470 Element old = enclosingElement; 471 try { 472 enclosingElement = ((JCMethodDecl) node).sym; 473 return super.visitMethod(node, p); 474 } finally { 475 enclosingElement = old; 476 } 477 } 478 479 private void addUsage (final String ownerName, final String className, final Map <String ,Map <String ,Set <ClassIndexImpl.UsageType>>> map, final ClassIndexImpl.UsageType type) { 480 assert className != null; 481 assert map != null; 482 assert type != null; 483 Map <String ,Set <ClassIndexImpl.UsageType>> tUsages = map.get(ownerName); 484 if (tUsages == null) { 485 tUsages = new HashMap <String ,Set <ClassIndexImpl.UsageType>> (); 486 map.put(ownerName,tUsages); 487 } 488 Set <ClassIndexImpl.UsageType> usageType = tUsages.get (className); 489 if (usageType == null) { 490 usageType = EnumSet.noneOf(ClassIndexImpl.UsageType.class); 491 tUsages.put (className, usageType); 492 } 493 usageType.add (type); 494 } 495 496 private boolean hasErrorName (Symbol cs) { 497 while (cs != null) { 498 if (cs.name == errorName) { 499 return true; 500 } 501 cs = cs.getEnclosingElement(); 502 } 503 return false; 504 } 505 506 private static String encodeClassName (final Symbol sym) { 507 assert sym instanceof Symbol.ClassSymbol; 508 TypeElement toEncode = null; 509 final TypeMirror type = ((Symbol.ClassSymbol)sym).asType(); 510 if (sym.getEnclosingElement().getKind() == ElementKind.TYPE_PARAMETER) { 511 if (type.getKind() == TypeKind.ARRAY) { 512 TypeMirror ctype = ((ArrayType) type).getComponentType(); 513 if (ctype.getKind() == TypeKind.DECLARED) { 514 toEncode = (TypeElement)((DeclaredType)ctype).asElement(); 515 } 516 } 517 } 518 else { 519 toEncode = (TypeElement) sym; 520 } 521 return toEncode == null ? null : ClassFileUtil.encodeClassName(toEncode); 522 } 523 } 524 525 } 526 | Popular Tags |