import org.jetbrains.jps.service.JpsServiceManager;
import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
import javax.tools.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
}
private static final class MyTaskListener implements TaskListener {
- private JCTree.JCCompilationUnit myCurrentCompilationUnit;
private final JavacFileReferencesRegistrar[] myFullASTListeners;
private final JavacFileReferencesRegistrar[] myOnlyImportsListeners;
private final JavacTreeRefScanner myAstScanner;
- private int myCurrentSize;
private Name myAsteriks;
+ private int myRemainDecls;
+ private JCTree.JCCompilationUnit myCurrentCompilationUnit;
+ private Set<JavacRefSymbol> myCollectedReferences;
+ private Set<JavacRefSymbol> myCollectedDefinitions;
+
public MyTaskListener(JavacFileReferencesRegistrar[] fullASTListenerArray, JavacFileReferencesRegistrar[] importsListenerArray) {
myFullASTListeners = fullASTListenerArray;
myOnlyImportsListeners = importsListenerArray;
public void finished(TaskEvent e) {
try {
if (e.getKind() == TaskEvent.Kind.ANALYZE) {
- // javac creates event on each processed class not file
+ // javac creates an event on each processed top level declared class not file
if (myCurrentCompilationUnit != e.getCompilationUnit()) {
myCurrentCompilationUnit = (JCTree.JCCompilationUnit)e.getCompilationUnit();
- myCurrentSize = myCurrentCompilationUnit.getTypeDecls().size() - 1;
+ myCollectedDefinitions = new THashSet<JavacRefSymbol>();
+ myCollectedReferences = new THashSet<JavacRefSymbol>();
+ myRemainDecls = myCurrentCompilationUnit.getTypeDecls().size() - 1;
+ scanImports(myCurrentCompilationUnit, myCollectedReferences);
+ for(JavacFileReferencesRegistrar r: myOnlyImportsListeners) {
+ r.registerFile(e.getSourceFile(), myCollectedReferences, myCollectedDefinitions);
+ }
}
else {
- myCurrentSize--;
+ myRemainDecls--;
}
- if (myCurrentSize == 0) {
- final JavaFileObject sourceFile = e.getSourceFile();
- final Set<JavacRefSymbol> symbols = new THashSet<JavacRefSymbol>();
- scanImports(myCurrentCompilationUnit, symbols);
- for (JavacFileReferencesRegistrar listener : myOnlyImportsListeners) {
- listener.registerFile(sourceFile, symbols, Collections.<JavacRefSymbol>emptySet());
+ JavacTreeScannerSink sink = new JavacTreeScannerSink() {
+ @Override
+ public void sinkReference(JavacRefSymbol ref) {
+ myCollectedReferences.add(ref);
}
- if (myFullASTListeners.length != 0) {
- final Collection<JavacRefSymbol> defs = new ArrayList<JavacRefSymbol>();
- myAstScanner.scan(myCurrentCompilationUnit, new JavacTreeScannerSink() {
- @Override
- public void sinkReference(JavacRefSymbol ref) {
- symbols.add(ref);
- }
- @Override
- public void sinkDeclaration(JavacRefSymbol def) {
- defs.add(def);
- }
- });
- for (JavacFileReferencesRegistrar listener : myFullASTListeners) {
- listener.registerFile(sourceFile, symbols, defs);
+ @Override
+ public void sinkDeclaration(JavacRefSymbol def) {
+ myCollectedDefinitions.add(def);
+ }
+ };
+
+ if (myFullASTListeners.length != 0) {
+ TypeElement analyzedElement = e.getTypeElement();
+ for (JCTree tree : myCurrentCompilationUnit.getTypeDecls()) {
+ if (tree.type != null && tree.type.tsym == analyzedElement) {
+ myAstScanner.scan(tree, sink);
}
}
}
+
+ if (myRemainDecls == 0) {
+ if (myFullASTListeners.length != 0) {
+ for (JCTree.JCAnnotation annotation : myCurrentCompilationUnit.getPackageAnnotations()) {
+ myAstScanner.scan(annotation, sink);
+ }
+ }
+
+ for(JavacFileReferencesRegistrar r: myFullASTListeners) {
+ r.registerFile(e.getSourceFile(), myCollectedReferences, myCollectedDefinitions);
+ }
+
+ myCurrentCompilationUnit = null;
+ myCollectedDefinitions = null;
+ myCollectedReferences = null;
+ }
+
}
}
catch (Exception ex) {
--- /dev/null
+import java.util.Collections;
+
+class Foo {
+
+ void m() {
+ Collections.emptySet();
+ }
+
+}
+
+class Boo {
+
+ void m() {
+ Collections.emptyList();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+Backward Hierarchy:
+java.lang.Object -> Boo Foo
+
+Backward References:
+Boo in Foo
+Boo.<init>(0) in Foo
+Boo.m(0) in Foo
+Foo in Foo
+Foo.<init>(0) in Foo
+Foo.m(0) in Foo
+java.lang.Object.<init>(0) in Foo
+java.util.Collections in Foo
+java.util.Collections.emptyList(0) in Foo
+java.util.Collections.emptySet(0) in Foo
+
+Class Definitions:
+Boo in Foo
+Foo in Foo
\ No newline at end of file
--- /dev/null
+import java.util.Collections;
+
+class Foo {
+
+ void m() {
+ Collections.emptyList();
+ }
+
+ static class Bar {
+
+ void m() {
+ Collections.emptyMap();
+ }
+
+ }
+
+}
+
+class Boo {
+
+ static class Baz {
+
+ void m() {
+ Collections.singleton(null);
+ }
+
+ }
+
+ void m() {
+ Collections.emptySet();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+Backward Hierarchy:
+java.lang.Object -> Boo Boo$Baz Foo Foo$Bar
+
+Backward References:
+Boo in Foo
+Boo$Baz in Foo
+Boo$Baz.<init>(0) in Foo
+Boo$Baz.m(0) in Foo
+Boo.<init>(0) in Foo
+Boo.m(0) in Foo
+Foo in Foo
+Foo$Bar in Foo
+Foo$Bar.<init>(0) in Foo
+Foo$Bar.m(0) in Foo
+Foo.<init>(0) in Foo
+Foo.m(0) in Foo
+java.lang.Object.<init>(0) in Foo
+java.util.Collections in Foo
+java.util.Collections.emptyList(0) in Foo
+java.util.Collections.emptyMap(0) in Foo
+java.util.Collections.emptySet(0) in Foo
+java.util.Collections.singleton(1) in Foo
+
+Class Definitions:
+Boo in Foo
+Boo$Baz in Foo
+Foo in Foo
+Foo$Bar in Foo
\ No newline at end of file
Backward References:
java.lang.Deprecated in package-info
+java.lang.Object.<init>(0) in package-info
myPackage.A in package-info
myPackage.A.<init>(0) in package-info
buildAllModules()
assertIndexEquals("fileDeletedIndex.txt")
}
+
+ fun testCompilationUnitContains2Decls() {
+ assertIndexOnRebuild("Foo.java")
+ }
+
+ fun testNestedClasses() {
+ assertIndexOnRebuild("Foo.java")
+ }
}
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
-import com.intellij.testFramework.UsefulTestCase
+import com.intellij.openapi.vfs.VfsUtil
import com.intellij.util.PathUtil
+import com.intellij.util.io.PersistentStringEnumerator
import com.sun.tools.javac.util.Convert
-import junit.framework.TestCase
import org.jetbrains.jps.backwardRefs.BackwardReferenceIndexWriter
import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator
import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex
protected fun assertIndexEquals(expectedIndexDumpFile: String) {
- val expectedIndex = FileUtil.loadFile(File(testDataRootPath + "/" + getTestName(true) + "/" + expectedIndexDumpFile), CharsetToolkit.UTF8_CHARSET)
- val actualIndex = indexAsText()
- TestCase.assertTrue(String.CASE_INSENSITIVE_ORDER.compare(expectedIndex, actualIndex) == 0)
+ assertSameLinesWithFile(testDataRootPath + "/" + getTestName(true) + "/" + expectedIndexDumpFile, indexAsText())
}
protected fun indexAsText(): String {
val inheritorsText = mutableListOf<String>()
inheritors.forEach { id ->
inheritorsText.add(id.asText(nameEnumerator))
- true
}
inheritorsText.sort()
hierarchyText.add(superClassName + " -> " + inheritorsText.joinToString(separator = " "))
index.backwardReferenceMap.forEachEntry { usage, files ->
val referents = mutableListOf<String>()
files.forEach { id ->
- val file = File(fileEnumerator.valueOf(id))
- val fileName = FileUtil.getNameWithoutExtension(file)
- referents.add(fileName)
+ referents.add(id.asFileName(fileEnumerator))
}
referents.sort()
referencesText.add(usage.asText(nameEnumerator) + " in " + referents.joinToString(separator = " "))
index.backwardClassDefinitionMap.forEachEntry { usage, files ->
val definitionFiles = mutableListOf<String>()
files.forEach { id ->
- val file = File(fileEnumerator.valueOf(id))
- val fileName = FileUtil.getNameWithoutExtension(file)
- definitionFiles.add(fileName)
+ definitionFiles.add(id.asFileName(fileEnumerator))
}
definitionFiles.sort()
classDefs.add(usage.asText(nameEnumerator) + " in " + definitionFiles.joinToString(separator = " "))
private fun getTestDataPath() = testDataRootPath + "/" + getTestName(true) + "/"
- private fun Int.asName(byteArrayEnumerator: ByteArrayEnumerator): String = Convert.utf2string(
- byteArrayEnumerator.valueOf(this))
+ private fun Int.asName(byteArrayEnumerator: ByteArrayEnumerator): String = Convert.utf2string(byteArrayEnumerator.valueOf(this))
private fun CompilerBackwardReferenceIndex.LightDefinition.asText(byteArrayEnumerator: ByteArrayEnumerator) = this.usage.asText(byteArrayEnumerator)
is LightUsage.LightFunExprUsage -> "fun_expr(" + this.name.asName(byteArrayEnumerator) + ")"
else -> throw UnsupportedOperationException()
}
+
+ private fun Int.asFileName(fileNameEnumerator: PersistentStringEnumerator) = FileUtil.getNameWithoutExtension(File(fileNameEnumerator.valueOf(this)).canonicalFile)
}
\ No newline at end of file