do not add private members references to index + add functional expressions to index
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / javac / ast / JavacReferencesCollector.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.jps.javac.ast;
17
18 import com.intellij.util.ReflectionUtil;
19 import com.intellij.util.SmartList;
20 import com.sun.source.tree.Tree;
21 import com.sun.source.util.JavacTask;
22 import com.sun.source.util.TaskEvent;
23 import com.sun.source.util.TaskListener;
24 import com.sun.tools.javac.code.Symbol;
25 import com.sun.tools.javac.tree.JCTree;
26 import com.sun.tools.javac.util.ClientCodeException;
27 import com.sun.tools.javac.util.Name;
28 import gnu.trove.THashSet;
29 import org.jetbrains.jps.javac.ast.api.JavacDefSymbol;
30 import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
31 import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
32 import org.jetbrains.jps.service.JpsServiceManager;
33
34 import javax.lang.model.element.ElementKind;
35 import javax.tools.*;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.util.*;
39
40 public class JavacReferencesCollector {
41   public static void installOn(JavacTask task) {
42     List<JavacFileReferencesRegistrar> fullASTListeners = new SmartList<JavacFileReferencesRegistrar>();
43     List<JavacFileReferencesRegistrar> onlyImportsListeners = new SmartList<JavacFileReferencesRegistrar>();
44     for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
45       if (!listener.initialize()) {
46         continue;
47       }
48       (listener.onlyImports() ? onlyImportsListeners : fullASTListeners).add(listener);
49     }
50
51     final JavacFileReferencesRegistrar[] fullASTListenerArray = fullASTListeners.toArray(new JavacFileReferencesRegistrar[fullASTListeners.size()]);
52     final JavacFileReferencesRegistrar[] onlyImportsListenerArray = onlyImportsListeners.toArray(new JavacFileReferencesRegistrar[onlyImportsListeners.size()]);
53     if (fullASTListenerArray.length == 0 && onlyImportsListenerArray.length == 0) {
54       return;
55     }
56
57     Method addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "addTaskListener", TaskListener.class); // jdk >= 8
58     if (addTaskMethod == null) {
59       addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "setTaskListener", TaskListener.class); // jdk 6-7
60     }
61     assert addTaskMethod != null;
62
63     try {
64       addTaskMethod.invoke(task, new MyTaskListener(fullASTListenerArray, onlyImportsListenerArray));
65     }
66     catch (IllegalAccessException e) {
67       throw new RuntimeException(e);
68     }
69     catch (InvocationTargetException e) {
70       throw new RuntimeException(e);
71     }
72   }
73
74   private static final class MyTaskListener implements TaskListener {
75     private JCTree.JCCompilationUnit myCurrentCompilationUnit;
76     private final JavacFileReferencesRegistrar[] myFullASTListeners;
77     private final JavacFileReferencesRegistrar[] myOnlyImportsListeners;
78     private final JavacTreeRefScanner myAstScanner;
79
80     private int myCurrentSize;
81     private Name myAsteriks;
82
83     public MyTaskListener(JavacFileReferencesRegistrar[] fullASTListenerArray, JavacFileReferencesRegistrar[] importsListenerArray) {
84       myFullASTListeners = fullASTListenerArray;
85       myOnlyImportsListeners = importsListenerArray;
86       myAstScanner = JavacTreeRefScanner.createASTScanner();
87     }
88
89     @Override
90     public void started(TaskEvent e) {
91
92     }
93
94     @Override
95     public void finished(TaskEvent e) {
96       try {
97         if (e.getKind() == TaskEvent.Kind.ANALYZE) {
98           // javac creates event on each processed class not file
99           if (myCurrentCompilationUnit != e.getCompilationUnit()) {
100             myCurrentCompilationUnit = (JCTree.JCCompilationUnit)e.getCompilationUnit();
101             myCurrentSize = myCurrentCompilationUnit.getTypeDecls().size() - 1;
102           }
103           else {
104             myCurrentSize--;
105           }
106
107           if (myCurrentSize == 0) {
108             final JavaFileObject sourceFile = e.getSourceFile();
109             final Set<JavacRefSymbol> symbols = new THashSet<JavacRefSymbol>();
110             scanImports(myCurrentCompilationUnit, symbols);
111             for (JavacFileReferencesRegistrar listener : myOnlyImportsListeners) {
112               listener.registerFile(sourceFile, symbols, Collections.<JavacDefSymbol>emptySet());
113             }
114             if (myFullASTListeners.length != 0) {
115               final Collection<JavacDefSymbol> defs = new ArrayList<JavacDefSymbol>();
116               myAstScanner.scan(myCurrentCompilationUnit, new JavacTreeScannerSink() {
117                 @Override
118                 public void sinkReference(JavacRefSymbol ref) {
119                   symbols.add(ref);
120                 }
121
122                 @Override
123                 public void sinkDeclaration(JavacDefSymbol def) {
124                   defs.add(def);
125                 }
126               });
127               for (JavacFileReferencesRegistrar listener : myFullASTListeners) {
128                 listener.registerFile(sourceFile, symbols, defs);
129               }
130             }
131           }
132         }
133       }
134       catch (Exception ex) {
135         throw new ClientCodeException(ex);
136       }
137     }
138
139     private Name getAsteriksFromCurrentNameTable(Name tableRepresentative) {
140       if (myAsteriks == null) {
141         myAsteriks = tableRepresentative.table.fromChars(new char[]{'*'}, 0, 1);
142       }
143       return myAsteriks;
144     }
145
146     private void scanImports(JCTree.JCCompilationUnit compilationUnit, Set<JavacRefSymbol> symbols) {
147       for (JCTree.JCImport anImport : compilationUnit.getImports()) {
148         final JCTree.JCFieldAccess id = (JCTree.JCFieldAccess)anImport.getQualifiedIdentifier();
149         final Symbol sym = id.sym;
150         if (sym == null) {
151           final JCTree.JCExpression qExpr = id.getExpression();
152           if (qExpr instanceof JCTree.JCFieldAccess) {
153             final JCTree.JCFieldAccess classImport = (JCTree.JCFieldAccess)qExpr;
154             final Symbol ownerSym = classImport.sym;
155             final Name name = id.getIdentifier();
156             if (name != getAsteriksFromCurrentNameTable(name)) {
157               // member import
158               for (Symbol memberSymbol : ownerSym.members().getElements()) {
159                 if (memberSymbol.getSimpleName() == name) {
160                   symbols.add(new JavacRefSymbol(memberSymbol, Tree.Kind.IMPORT));
161                 }
162               }
163             }
164             collectClassImports(ownerSym, symbols);
165           }
166         } else {
167           // class import
168           collectClassImports(sym, symbols);
169         }
170       }
171     }
172   }
173
174   private static void collectClassImports(Symbol baseImport, Set<JavacRefSymbol> collector) {
175     for (Symbol symbol = baseImport;
176          symbol != null && symbol.getKind() != ElementKind.PACKAGE;
177          symbol = symbol.owner) {
178       collector.add(new JavacRefSymbol(symbol, Tree.Kind.IMPORT));
179     }
180   }
181 }