Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2012 Oracle and/or its affiliates. All rights reserved. |
5 |
* |
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates. |
7 |
* Other names may be trademarks of their respective owners. |
8 |
* |
9 |
* The contents of this file are subject to the terms of either the GNU |
10 |
* General Public License Version 2 only ("GPL") or the Common |
11 |
* Development and Distribution License("CDDL") (collectively, the |
12 |
* "License"). You may not use this file except in compliance with the |
13 |
* License. You can obtain a copy of the License at |
14 |
* http://www.netbeans.org/cddl-gplv2.html |
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
16 |
* specific language governing permissions and limitations under the |
17 |
* License. When distributing the software, include this License Header |
18 |
* Notice in each file and include the License file at |
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this |
20 |
* particular file as subject to the "Classpath" exception as provided |
21 |
* by Oracle in the GPL Version 2 section of the License file that |
22 |
* accompanied this code. If applicable, add the following below the |
23 |
* License Header, with the fields enclosed by brackets [] replaced by |
24 |
* your own identifying information: |
25 |
* "Portions Copyrighted [year] [name of copyright owner]" |
26 |
* |
27 |
* If you wish your version of this file to be governed by only the CDDL |
28 |
* or only the GPL Version 2, indicate your decision by adding |
29 |
* "[Contributor] elects to include this software in this distribution |
30 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
31 |
* single choice of license, a recipient has the option to distribute |
32 |
* your version of this file under either the CDDL, the GPL Version 2 or |
33 |
* to extend the choice of license to its licensees as provided above. |
34 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
35 |
* Version 2 license, then the option applies only if the new code is |
36 |
* made subject to such option by the copyright holder. |
37 |
* |
38 |
* Contributor(s): |
39 |
* |
40 |
* Portions Copyrighted 2012 Sun Microsystems, Inc. |
41 |
*/ |
42 |
package org.netbeans.modules.java.source.indexing.neo4j; |
43 |
|
44 |
import com.sun.source.tree.ClassTree; |
45 |
import com.sun.source.tree.CompilationUnitTree; |
46 |
import com.sun.source.tree.ErroneousTree; |
47 |
import com.sun.source.tree.IdentifierTree; |
48 |
import com.sun.source.tree.ImportTree; |
49 |
import com.sun.source.tree.MemberSelectTree; |
50 |
import com.sun.source.tree.MethodTree; |
51 |
import com.sun.source.tree.NewClassTree; |
52 |
import com.sun.source.tree.ParameterizedTypeTree; |
53 |
import com.sun.source.tree.Tree; |
54 |
import com.sun.source.tree.VariableTree; |
55 |
import com.sun.source.util.TreeScanner; |
56 |
import com.sun.tools.javac.api.JavacTaskImpl; |
57 |
import com.sun.tools.javac.code.Kinds; |
58 |
import com.sun.tools.javac.code.Symbol; |
59 |
import com.sun.tools.javac.code.Type; |
60 |
import com.sun.tools.javac.tree.JCTree; |
61 |
import com.sun.tools.javac.util.Name; |
62 |
import com.sun.tools.javac.util.Names; |
63 |
import java.net.MalformedURLException; |
64 |
import java.net.URI; |
65 |
import java.net.URL; |
66 |
import java.util.ArrayDeque; |
67 |
import java.util.BitSet; |
68 |
import java.util.Deque; |
69 |
import java.util.HashMap; |
70 |
import java.util.HashSet; |
71 |
import java.util.List; |
72 |
import java.util.Map; |
73 |
import java.util.Set; |
74 |
import java.util.Stack; |
75 |
import java.util.logging.Level; |
76 |
import java.util.logging.Logger; |
77 |
import javax.lang.model.element.Element; |
78 |
import javax.lang.model.element.ElementKind; |
79 |
import javax.lang.model.element.ExecutableElement; |
80 |
import javax.lang.model.element.TypeElement; |
81 |
import javax.lang.model.type.ArrayType; |
82 |
import javax.lang.model.type.DeclaredType; |
83 |
import javax.lang.model.type.TypeKind; |
84 |
import javax.lang.model.type.TypeMirror; |
85 |
import javax.tools.JavaFileManager; |
86 |
import javax.tools.JavaFileObject; |
87 |
import javax.tools.StandardLocation; |
88 |
import org.netbeans.api.annotations.common.CheckForNull; |
89 |
import org.netbeans.api.annotations.common.NonNull; |
90 |
import org.netbeans.api.annotations.common.NullAllowed; |
91 |
import org.netbeans.api.java.classpath.ClassPath; |
92 |
import org.netbeans.api.java.source.ElementHandle; |
93 |
import org.netbeans.api.java.source.SourceUtils; |
94 |
import org.netbeans.modules.java.source.indexing.JavaCustomIndexer; |
95 |
import org.netbeans.modules.java.source.indexing.TransactionContext; |
96 |
import org.netbeans.modules.java.source.parsing.FileObjects; |
97 |
import org.netbeans.modules.java.source.usages.ClassFileUtil; |
98 |
import org.netbeans.modules.java.source.usages.ClassIndexImpl; |
99 |
import org.netbeans.modules.java.source.usages.Pair; |
100 |
import org.openide.filesystems.FileObject; |
101 |
import org.openide.filesystems.FileUtil; |
102 |
import org.openide.filesystems.URLMapper; |
103 |
import org.openide.util.Exceptions; |
104 |
|
105 |
/** |
106 |
* |
107 |
* @author tom |
108 |
*/ |
109 |
public class Neo4jAnalyzer extends TreeScanner<Void, Void> { |
110 |
|
111 |
private static final Logger LOG = Logger.getLogger(Neo4jAnalyzer.class.getName()); |
112 |
|
113 |
enum State { |
114 |
|
115 |
EXTENDS, IMPLEMENTS, GT, OTHER, IMPORT, PACKAGE_ANN |
116 |
}; |
117 |
private final Neo4jIndexTransaction neoTx; |
118 |
private final Stack<ClassNode> activeClass; |
119 |
private final Name errorName; |
120 |
private final Name pkgImportName; |
121 |
private final URL siblingUrl; |
122 |
private final String sourceName; |
123 |
private final boolean signatureFiles; |
124 |
private final Set<? super Pair<String, String>> topLevels; |
125 |
private final Set<? super ElementHandle<TypeElement>> newTypes; |
126 |
private final Set<Symbol> imports; |
127 |
private final Set<Symbol> staticImports; |
128 |
private final Set<Symbol> unusedPkgImports; |
129 |
private final Set<Pair<Symbol, ClassIndexImpl.UsageType>> packageAnnotations; |
130 |
private final Set<CharSequence> importIdents; |
131 |
private final Set<CharSequence> packageAnnotationIdents; |
132 |
private final boolean virtual; |
133 |
private final boolean[] mainMethod; |
134 |
private final Neo4jIndex index; |
135 |
private final RootNode rootNode; |
136 |
private boolean isStaticImport; |
137 |
private boolean isPkgImport; |
138 |
private State state; |
139 |
private Element enclosingElement = null; |
140 |
private Set<String> rsList; //List of references from source in case when the source has more top levels or is wrongly packaged |
141 |
private boolean crossedTopLevel; //True when the visitor already reached the correctly packaged top level |
142 |
private PackageNode packageNode; |
143 |
private CompilationUnitTree cu; |
144 |
private Map<ClassNode, Map<String, BitSet>> usages; |
145 |
|
146 |
public Neo4jAnalyzer( |
147 |
final JavacTaskImpl jt, |
148 |
final JavaFileManager manager, |
149 |
final JavaCustomIndexer.CompileTuple tuple, |
150 |
final Set<? super ElementHandle<TypeElement>> newTypes, |
151 |
final boolean[] mainMethod) throws MalformedURLException, IllegalArgumentException { |
152 |
|
153 |
assert jt != null; |
154 |
assert manager != null; |
155 |
assert tuple != null; |
156 |
assert mainMethod != null; |
157 |
|
158 |
this.activeClass = new Stack<ClassNode>(); |
159 |
this.imports = new HashSet<Symbol>(); |
160 |
this.staticImports = new HashSet<Symbol>(); |
161 |
this.unusedPkgImports = new HashSet<Symbol>(); |
162 |
this.importIdents = new HashSet<CharSequence>(); |
163 |
this.packageAnnotationIdents = new HashSet<CharSequence>(); |
164 |
this.packageAnnotations = new HashSet<Pair<Symbol, ClassIndexImpl.UsageType>>(); |
165 |
final Names names = Names.instance(jt.getContext()); |
166 |
this.errorName = names.error; |
167 |
this.pkgImportName = names.asterisk; |
168 |
this.state = State.OTHER; |
169 |
this.signatureFiles = true; |
170 |
this.virtual = tuple.virtual; |
171 |
this.siblingUrl = virtual ? tuple.indexable.getURL() : tuple.jfo.toUri().toURL(); |
172 |
this.sourceName = inferBinaryName(manager, tuple.jfo); |
173 |
this.topLevels = null; |
174 |
this.newTypes = newTypes; |
175 |
this.mainMethod = mainMethod; |
176 |
index = Neo4jIndex.getDefault(); |
177 |
neoTx = TransactionContext.get().get(Neo4jIndexTransaction.class); |
178 |
rootNode = neoTx.getRootNode(); |
179 |
usages = new HashMap<ClassNode, Map<String, BitSet>>(); |
180 |
} |
181 |
|
182 |
@Override |
183 |
@CheckForNull |
184 |
public Void scan(@NonNull final Tree node, @NullAllowed final Void nil) { |
185 |
if (node == null) { |
186 |
return null; |
187 |
} |
188 |
super.scan(node, nil); |
189 |
return null; |
190 |
} |
191 |
|
192 |
@Override |
193 |
@CheckForNull |
194 |
public Void visitCompilationUnit(@NonNull final CompilationUnitTree node, @NullAllowed final Void nil) { |
195 |
this.cu = node; |
196 |
State oldState = state; |
197 |
try { |
198 |
state = State.PACKAGE_ANN; |
199 |
scan(node.getPackageAnnotations(), nil); |
200 |
scan(node.getPackageName(), nil); |
201 |
packageNode = index.getOrCreatePackage(rootNode, node.getPackageName().toString()); |
202 |
state = State.IMPORT; |
203 |
scan(node.getImports(), nil); |
204 |
} finally { |
205 |
state = oldState; |
206 |
} |
207 |
scan(node.getTypeDecls(), nil); |
208 |
|
209 |
ClassNode classNode = null; |
210 |
if (!imports.isEmpty() |
211 |
|| !staticImports.isEmpty() |
212 |
|| !unusedPkgImports.isEmpty()) { |
213 |
//Empty file |
214 |
String className = getResourceSimpleName(node); |
215 |
if (className != null) { |
216 |
classNode = index.createClass( |
217 |
packageNode, |
218 |
className, |
219 |
className, |
220 |
ElementKind.CLASS, |
221 |
null); |
222 |
} |
223 |
addAndClearImports(classNode); |
224 |
addAndClearUnusedPkgImports(classNode); |
225 |
} |
226 |
|
227 |
if (!packageAnnotations.isEmpty()) { |
228 |
if (classNode == null) { |
229 |
String className = getResourceSimpleName(node); |
230 |
if (className != null) { |
231 |
classNode = index.createClass( |
232 |
packageNode, |
233 |
className, |
234 |
className, |
235 |
ElementKind.CLASS, |
236 |
null); |
237 |
for (Pair<Symbol, ClassIndexImpl.UsageType> usage : packageAnnotations) { |
238 |
addUsage(classNode, usage.first, usage.second); |
239 |
} |
240 |
//TODO: for (CharSequence ident : packageAnnotationIdents) { |
241 |
// addIdent(name, ident, p, false); |
242 |
// } |
243 |
} |
244 |
} |
245 |
packageAnnotations.clear(); |
246 |
packageAnnotationIdents.clear(); |
247 |
} |
248 |
storeUsages(); |
249 |
return null; |
250 |
} |
251 |
|
252 |
@Override |
253 |
@CheckForNull |
254 |
public Void visitClass(@NonNull final ClassTree node, @NullAllowed final Void nil) { |
255 |
final Symbol.ClassSymbol sym = ((JCTree.JCClassDecl) node).sym; |
256 |
boolean errorInDecl = false; |
257 |
boolean errorIgnorSubtree = true; |
258 |
boolean topLevel = false; |
259 |
String className = null; |
260 |
ClassNode classNode = null; |
261 |
|
262 |
if (sym != null) { |
263 |
errorInDecl = hasErrorName(sym); |
264 |
if (errorInDecl) { |
265 |
if (!activeClass.isEmpty()) { |
266 |
classNode = activeClass.get(0); |
267 |
} else { |
268 |
topLevel = true; |
269 |
className = getResourceName(this.cu); |
270 |
if (className != null && !className.isEmpty()) { |
271 |
final String simpleName = className.substring(className.lastIndexOf('.') + 1); //NOI18N |
272 |
classNode = index.createClass( |
273 |
packageNode, |
274 |
simpleName, |
275 |
simpleName, |
276 |
ElementKind.CLASS, |
277 |
null); |
278 |
} else { |
279 |
LOG.log( |
280 |
Level.WARNING, |
281 |
"Cannot resolve {0} (class name: {1}), ignoring whole subtree.", //NOI18N |
282 |
new Object[]{ |
283 |
sym, |
284 |
className |
285 |
}); |
286 |
} |
287 |
} |
288 |
} else { |
289 |
final StringBuilder classNameBuilder = new StringBuilder(); |
290 |
ClassFileUtil.encodeClassName(sym, classNameBuilder, '.'); //NOI18N |
291 |
className = classNameBuilder.toString(); |
292 |
if (!className.isEmpty()) { |
293 |
ElementKind classType = sym.getKind(); |
294 |
String resourceName = null; |
295 |
topLevel = activeClass.isEmpty(); |
296 |
if (topLevel) { |
297 |
if (virtual || !className.equals(sourceName)) { |
298 |
if (signatureFiles && rsList == null) { |
299 |
rsList = new HashSet<String>(); |
300 |
if (crossedTopLevel) { |
301 |
rsList.add(sourceName); |
302 |
} |
303 |
} |
304 |
final StringBuilder rnBuilder = new StringBuilder(FileObjects.convertPackage2Folder(sourceName)); |
305 |
rnBuilder.append('.'); //NOI18N |
306 |
rnBuilder.append(FileObjects.getExtension(siblingUrl.getPath())); |
307 |
resourceName = rnBuilder.toString(); |
308 |
} else { |
309 |
crossedTopLevel = true; |
310 |
} |
311 |
} else { |
312 |
resourceName = activeClass.peek().getResourceName(); |
313 |
} |
314 |
classNode = index.createClass( |
315 |
packageNode, |
316 |
sym.getSimpleName().toString(), |
317 |
className.substring(className.lastIndexOf('.') + 1), //NOI18N |
318 |
classType, |
319 |
resourceName); |
320 |
} else { |
321 |
LOG.log( |
322 |
Level.WARNING, |
323 |
"Invalid symbol {0} (source: {1}), ignoring whole subtree.", //NOI18N |
324 |
new Object[]{ |
325 |
sym, |
326 |
siblingUrl |
327 |
}); |
328 |
} |
329 |
} |
330 |
} |
331 |
if (classNode != null) { |
332 |
activeClass.push(classNode); |
333 |
errorIgnorSubtree = false; |
334 |
if (className != null) { |
335 |
if (topLevel) { |
336 |
if (topLevels != null) { |
337 |
topLevels.add(Pair.<String, String>of(className, classNode.getResourceName())); |
338 |
} |
339 |
addAndClearImports(classNode); |
340 |
} |
341 |
addUsage(classNode, className, ClassIndexImpl.UsageType.TYPE_REFERENCE); |
342 |
// index only simple name, not FQN for classes |
343 |
//TODO: addIdent(classNode, simpleName, nil, true); |
344 |
if (newTypes != null) { |
345 |
newTypes.add((ElementHandle<TypeElement>) ElementHandle.createTypeElementHandle(ElementKind.CLASS, className)); |
346 |
} |
347 |
} |
348 |
} |
349 |
if (!errorIgnorSubtree) { |
350 |
Element old = enclosingElement; |
351 |
try { |
352 |
enclosingElement = sym; |
353 |
scan(node.getModifiers(), nil); |
354 |
scan(node.getTypeParameters(), nil); |
355 |
state = errorInDecl ? State.OTHER : State.EXTENDS; |
356 |
scan(node.getExtendsClause(), nil); |
357 |
state = errorInDecl ? State.OTHER : State.IMPLEMENTS; |
358 |
scan(node.getImplementsClause(), nil); |
359 |
state = State.OTHER; |
360 |
scan(node.getMembers(), nil); |
361 |
activeClass.pop(); |
362 |
} finally { |
363 |
enclosingElement = old; |
364 |
} |
365 |
} |
366 |
if (!errorInDecl && this.rsList != null) { |
367 |
this.rsList.add(className); |
368 |
} |
369 |
if (topLevel) { |
370 |
addAndClearUnusedPkgImports(classNode); |
371 |
} |
372 |
return null; |
373 |
} |
374 |
|
375 |
@Override |
376 |
@CheckForNull |
377 |
public Void visitNewClass(@NonNull final NewClassTree node, @NullAllowed final Void nil) { |
378 |
final Symbol sym = ((JCTree.JCNewClass) node).constructor; |
379 |
if (sym != null) { |
380 |
final Symbol owner = sym.getEnclosingElement(); |
381 |
if (owner != null && owner.getKind().isClass()) { |
382 |
addUsage( |
383 |
activeClass.peek(), |
384 |
owner, |
385 |
ClassIndexImpl.UsageType.METHOD_REFERENCE); |
386 |
} |
387 |
} |
388 |
return super.visitNewClass(node, nil); |
389 |
} |
390 |
|
391 |
@Override |
392 |
@CheckForNull |
393 |
public Void visitErroneous(@NonNull final ErroneousTree tree, @NullAllowed final Void nil) { |
394 |
List<? extends Tree> trees = tree.getErrorTrees(); |
395 |
for (Tree t : trees) { |
396 |
this.scan(t, nil); |
397 |
} |
398 |
return null; |
399 |
} |
400 |
|
401 |
@Override |
402 |
@CheckForNull |
403 |
public Void visitMethod(@NonNull final MethodTree node, @NullAllowed final Void nil) { |
404 |
Element old = enclosingElement; |
405 |
try { |
406 |
enclosingElement = ((JCTree.JCMethodDecl) node).sym; |
407 |
if (enclosingElement != null && enclosingElement.getKind() == ElementKind.METHOD) { |
408 |
mainMethod[0] |= SourceUtils.isMainMethod((ExecutableElement) enclosingElement); |
409 |
// do not add idents for constructors, they always match their class' name, which is added as an ident separately |
410 |
//todo: addIdent(activeClass.peek(), node.getName(), p, true); |
411 |
} |
412 |
return super.visitMethod(node, nil); |
413 |
} finally { |
414 |
enclosingElement = old; |
415 |
} |
416 |
} |
417 |
|
418 |
@Override |
419 |
@CheckForNull |
420 |
public Void visitVariable(@NonNull final VariableTree node, @NullAllowed final Void nil) { |
421 |
Symbol s = ((JCTree.JCVariableDecl) node).sym; |
422 |
if (s != null && s.owner != null && (s.owner.getKind().isClass() || s.owner.getKind().isInterface())) { |
423 |
//todo: addIdent(activeClass.peek(), node.getName(), p, true); |
424 |
} |
425 |
return super.visitVariable(node, nil); |
426 |
} |
427 |
|
428 |
@Override |
429 |
@CheckForNull |
430 |
public Void visitMemberSelect(@NonNull final MemberSelectTree node, @NullAllowed final Void nil) { |
431 |
handleVisitIdentSelect(((JCTree.JCFieldAccess) node).sym, node.getIdentifier()); |
432 |
State oldState = this.state; |
433 |
this.state = (this.state == State.IMPORT || state == State.PACKAGE_ANN) ? state : State.OTHER; |
434 |
Void ret = super.visitMemberSelect(node, nil); |
435 |
this.state = oldState; |
436 |
return ret; |
437 |
} |
438 |
|
439 |
@Override |
440 |
@CheckForNull |
441 |
public Void visitIdentifier(@NonNull final IdentifierTree node, @NonNull @NullAllowed final Void nil) { |
442 |
handleVisitIdentSelect(((JCTree.JCIdent) node).sym, node.getName()); |
443 |
return super.visitIdentifier(node, nil); |
444 |
} |
445 |
|
446 |
@Override |
447 |
@CheckForNull |
448 |
public Void visitImport(@NonNull final ImportTree node, @NullAllowed final Void nil) { |
449 |
this.isStaticImport = node.isStatic(); |
450 |
final Tree qit = node.getQualifiedIdentifier(); |
451 |
isPkgImport = qit.getKind() == Tree.Kind.MEMBER_SELECT && pkgImportName == (((MemberSelectTree) qit).getIdentifier()); |
452 |
final Void ret = super.visitImport(node, nil); |
453 |
isStaticImport = isPkgImport = false; |
454 |
return ret; |
455 |
} |
456 |
|
457 |
private void handleVisitIdentSelect( |
458 |
@NullAllowed final Symbol sym, |
459 |
@NonNull final CharSequence name) { |
460 |
if (!activeClass.empty()) { |
461 |
//todo: addIdent(activeClass.peek(), name, p, false); |
462 |
if (sym != null) { |
463 |
if (sym.kind == Kinds.ERR) { |
464 |
final Symbol owner = sym.getEnclosingElement(); |
465 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
466 |
addUsage(activeClass.peek(), owner, ClassIndexImpl.UsageType.TYPE_REFERENCE); |
467 |
} |
468 |
} |
469 |
if (sym.getKind().isClass() || sym.getKind().isInterface()) { |
470 |
switch (this.state) { |
471 |
case EXTENDS: |
472 |
addUsage(activeClass.peek(), sym, ClassIndexImpl.UsageType.SUPER_CLASS); |
473 |
break; |
474 |
case IMPLEMENTS: |
475 |
addUsage(activeClass.peek(), sym, ClassIndexImpl.UsageType.SUPER_INTERFACE); |
476 |
break; |
477 |
case OTHER: |
478 |
case GT: |
479 |
addUsage(activeClass.peek(), sym, ClassIndexImpl.UsageType.TYPE_REFERENCE); |
480 |
break; |
481 |
} |
482 |
} else if (sym.getKind().isField()) { |
483 |
final Symbol owner = sym.getEnclosingElement(); |
484 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
485 |
addUsage(activeClass.peek(), owner, ClassIndexImpl.UsageType.FIELD_REFERENCE); |
486 |
} |
487 |
recordTypeUsage(sym.asType()); |
488 |
} else if (sym.getKind() == ElementKind.CONSTRUCTOR || sym.getKind() == ElementKind.METHOD) { |
489 |
final Symbol owner = sym.getEnclosingElement(); |
490 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
491 |
addUsage(activeClass.peek(), owner, ClassIndexImpl.UsageType.METHOD_REFERENCE); |
492 |
} |
493 |
recordTypeUsage(((Symbol.MethodSymbol) sym).getReturnType()); |
494 |
} |
495 |
} |
496 |
} else { |
497 |
if (state == State.IMPORT) { |
498 |
importIdents.add(name); |
499 |
if (sym != null && (sym.getKind().isClass() || sym.getKind().isInterface())) { |
500 |
if (this.isStaticImport) { |
501 |
this.staticImports.add(sym); |
502 |
} else { |
503 |
this.imports.add(sym); |
504 |
} |
505 |
} else if (isPkgImport && sym != null && sym.getKind() == ElementKind.PACKAGE) { |
506 |
unusedPkgImports.add(sym); |
507 |
isPkgImport = false; |
508 |
} |
509 |
} else if (state == State.PACKAGE_ANN) { |
510 |
packageAnnotationIdents.add(name); |
511 |
if (sym != null) { |
512 |
if (sym.kind == Kinds.ERR) { |
513 |
final Symbol owner = sym.getEnclosingElement(); |
514 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
515 |
packageAnnotations.add(Pair.of(owner, ClassIndexImpl.UsageType.TYPE_REFERENCE)); |
516 |
} |
517 |
} |
518 |
if (sym.getKind().isClass() || sym.getKind().isInterface()) { |
519 |
packageAnnotations.add(Pair.of(sym, ClassIndexImpl.UsageType.TYPE_REFERENCE)); |
520 |
} else if (sym.getKind().isField()) { |
521 |
final Symbol owner = sym.getEnclosingElement(); |
522 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
523 |
packageAnnotations.add(Pair.of(owner, ClassIndexImpl.UsageType.FIELD_REFERENCE)); |
524 |
} |
525 |
} else if (sym.getKind() == ElementKind.CONSTRUCTOR || sym.getKind() == ElementKind.METHOD) { |
526 |
final Symbol owner = sym.getEnclosingElement(); |
527 |
if (owner.getKind().isClass() || owner.getKind().isInterface()) { |
528 |
packageAnnotations.add(Pair.of(owner, ClassIndexImpl.UsageType.METHOD_REFERENCE)); |
529 |
} |
530 |
} |
531 |
} |
532 |
} |
533 |
} |
534 |
} |
535 |
|
536 |
@Override |
537 |
@CheckForNull |
538 |
public Void visitParameterizedType(@NonNull final ParameterizedTypeTree node, @NullAllowed final Void nil) { |
539 |
scan(node.getType(), nil); |
540 |
State currState = this.state; |
541 |
this.state = State.GT; |
542 |
scan(node.getTypeArguments(), nil); |
543 |
this.state = currState; |
544 |
return null; |
545 |
} |
546 |
|
547 |
@NonNull |
548 |
private String inferBinaryName( |
549 |
@NonNull final JavaFileManager jfm, |
550 |
@NonNull final javax.tools.JavaFileObject jfo) throws IllegalArgumentException { |
551 |
String result = jfm.inferBinaryName(StandardLocation.SOURCE_PATH, jfo); |
552 |
if (result != null) { |
553 |
return result; |
554 |
} |
555 |
FileObject fo = null; |
556 |
ClassPath scp = null; |
557 |
try { |
558 |
fo = URLMapper.findFileObject(jfo.toUri().toURL()); |
559 |
if (fo != null) { |
560 |
scp = ClassPath.getClassPath(fo, ClassPath.SOURCE); |
561 |
if (scp != null) { |
562 |
result = scp.getResourceName(fo, '.', false); //NOI18N |
563 |
if (result != null) { |
564 |
return result; |
565 |
} |
566 |
} |
567 |
} |
568 |
} catch (MalformedURLException e) { |
569 |
//pass - throws IAE |
570 |
} |
571 |
throw new IllegalArgumentException(String.format("File: %s Type: %s FileObject: %s Sourcepath: %s", //NOI18N |
572 |
jfo.toUri().toString(), |
573 |
jfo.getClass().getName(), |
574 |
fo == null ? "<null>" : FileUtil.getFileDisplayName(fo), //NOI18N |
575 |
scp == null ? "<null>" : scp.toString())); //NOI18N |
576 |
} |
577 |
|
578 |
@CheckForNull |
579 |
private static String getResourceSimpleName(@NullAllowed final CompilationUnitTree cu) { |
580 |
final FileObject file = findFile(cu); |
581 |
if (file != null) { |
582 |
return file.getName(); |
583 |
} |
584 |
return null; |
585 |
} |
586 |
|
587 |
@CheckForNull |
588 |
private static String getResourceName (@NullAllowed final CompilationUnitTree cu) { |
589 |
final FileObject file = findFile(cu); |
590 |
if (file != null) { |
591 |
final ClassPath cp = ClassPath.getClassPath(file,ClassPath.SOURCE); |
592 |
if (cp != null) { |
593 |
return cp.getResourceName(file,'.',false); |
594 |
} |
595 |
} |
596 |
return null; |
597 |
} |
598 |
|
599 |
@CheckForNull |
600 |
private static FileObject findFile(@NullAllowed final CompilationUnitTree cu) { |
601 |
if (cu instanceof JCTree.JCCompilationUnit) { |
602 |
JavaFileObject jfo = ((JCTree.JCCompilationUnit)cu).sourcefile; |
603 |
if (jfo != null) { |
604 |
URI uri = jfo.toUri(); |
605 |
if (uri != null && uri.isAbsolute()) { |
606 |
try { |
607 |
FileObject fo = URLMapper.findFileObject(uri.toURL()); |
608 |
return fo; |
609 |
} catch (MalformedURLException e) { |
610 |
Exceptions.printStackTrace(e); |
611 |
} |
612 |
} |
613 |
} |
614 |
} |
615 |
return null; |
616 |
} |
617 |
|
618 |
private boolean hasErrorName(@NullAllowed Symbol cs) { |
619 |
while (cs != null) { |
620 |
if (cs.name == errorName) { |
621 |
return true; |
622 |
} |
623 |
cs = cs.getEnclosingElement(); |
624 |
} |
625 |
return false; |
626 |
} |
627 |
|
628 |
private void addAndClearImports( |
629 |
@NullAllowed final ClassNode classNode) { |
630 |
if (classNode != null) { |
631 |
for (Symbol s : imports) { |
632 |
addUsage( |
633 |
classNode, |
634 |
s, |
635 |
ClassIndexImpl.UsageType.TYPE_REFERENCE); |
636 |
} |
637 |
for (Symbol s : staticImports) { |
638 |
addUsage( |
639 |
classNode, |
640 |
s, |
641 |
ClassIndexImpl.UsageType.TYPE_REFERENCE, |
642 |
ClassIndexImpl.UsageType.METHOD_REFERENCE, |
643 |
ClassIndexImpl.UsageType.FIELD_REFERENCE); |
644 |
} |
645 |
//TODO: for (CharSequence s : importIdents) { |
646 |
// addIdent(nameOfCU, s, data, false); |
647 |
// } |
648 |
} |
649 |
imports.clear(); |
650 |
staticImports.clear(); |
651 |
importIdents.clear(); |
652 |
} |
653 |
|
654 |
private void addAndClearUnusedPkgImports( |
655 |
@NullAllowed final ClassNode classNode) { |
656 |
if (classNode != null) { |
657 |
for (Symbol s : unusedPkgImports) { |
658 |
final StringBuilder sb = new StringBuilder(); |
659 |
sb.append(s.getQualifiedName()); |
660 |
sb.append(".package-info"); //NOI18N |
661 |
addUsage(classNode, sb.toString(), ClassIndexImpl.UsageType.TYPE_REFERENCE); |
662 |
} |
663 |
} |
664 |
unusedPkgImports.clear(); |
665 |
} |
666 |
|
667 |
@CheckForNull |
668 |
private static String encodeClassName(@NonNull final Symbol sym) { |
669 |
assert sym instanceof Symbol.ClassSymbol; |
670 |
TypeElement toEncode = null; |
671 |
final TypeMirror type = ((Symbol.ClassSymbol) sym).asType(); |
672 |
if (sym.getEnclosingElement().getKind() == ElementKind.TYPE_PARAMETER) { |
673 |
if (type.getKind() == TypeKind.ARRAY) { |
674 |
TypeMirror ctype = ((ArrayType) type).getComponentType(); |
675 |
if (ctype.getKind() == TypeKind.DECLARED) { |
676 |
toEncode = (TypeElement) ((DeclaredType) ctype).asElement(); |
677 |
} |
678 |
} |
679 |
} else { |
680 |
toEncode = (TypeElement) sym; |
681 |
} |
682 |
return toEncode == null ? null : ClassFileUtil.encodeClassName(toEncode); |
683 |
} |
684 |
|
685 |
private void addUsage(@NonNull final ClassNode classNode, |
686 |
@NullAllowed final Symbol sym, |
687 |
@NonNull final ClassIndexImpl.UsageType... usageTypes) { |
688 |
assert classNode != null; |
689 |
assert usageTypes != null; |
690 |
if (sym != null) { |
691 |
final String className = encodeClassName(sym); |
692 |
for (ClassIndexImpl.UsageType usageType : usageTypes) { |
693 |
addUsage(classNode, className, usageType); |
694 |
} |
695 |
final Symbol encElm = sym.getEnclosingElement(); |
696 |
if (encElm.getKind() == ElementKind.PACKAGE) { |
697 |
unusedPkgImports.remove(encElm); |
698 |
} |
699 |
} |
700 |
} |
701 |
|
702 |
private void addUsage( |
703 |
@NonNull final ClassNode classNode, |
704 |
@NullAllowed final Symbol sym, |
705 |
@NonNull final ClassIndexImpl.UsageType usageType) { |
706 |
assert classNode != null; |
707 |
assert usageType != null; |
708 |
if (sym != null) { |
709 |
final String className = encodeClassName(sym); |
710 |
addUsage(classNode, className, usageType); |
711 |
final Symbol encElm = sym.getEnclosingElement(); |
712 |
if (encElm.getKind() == ElementKind.PACKAGE) { |
713 |
unusedPkgImports.remove(encElm); |
714 |
} |
715 |
} |
716 |
} |
717 |
|
718 |
private void addUsage( |
719 |
@NonNull final ClassNode user, |
720 |
@NullAllowed final String className, |
721 |
@NonNull final ClassIndexImpl.UsageType type) { |
722 |
if (className != null) { |
723 |
Map<String,BitSet> usagesForClass = usages.get(user); |
724 |
if (usagesForClass == null) { |
725 |
usagesForClass = new HashMap<String, BitSet>(); |
726 |
usages.put(user, usagesForClass); |
727 |
} |
728 |
BitSet usageTypes = usagesForClass.get(className); |
729 |
if (usageTypes == null) { |
730 |
usageTypes = new BitSet(ClassIndexImpl.UsageType.values().length); |
731 |
usagesForClass.put(className, usageTypes); |
732 |
} |
733 |
usageTypes.set(type.ordinal()); |
734 |
} |
735 |
} |
736 |
|
737 |
private void storeUsages() { |
738 |
for (Map.Entry<ClassNode,Map<String,BitSet>> cu : usages.entrySet()) { |
739 |
final ClassNode user = cu.getKey(); |
740 |
for (Map.Entry<String,BitSet> us : cu.getValue().entrySet()) { |
741 |
final String className = us.getKey(); |
742 |
TypeReferenceNode reference = neoTx.getCachedTypeReference(className); |
743 |
if (reference == null) { |
744 |
reference = index.getOrCreateTypeReference(className); |
745 |
neoTx.cacheTypeReference(className, reference); |
746 |
} |
747 |
user.uses(reference, us.getValue()); |
748 |
} |
749 |
} |
750 |
} |
751 |
|
752 |
private void recordTypeUsage(final TypeMirror type) { |
753 |
Deque<TypeMirror> types = new ArrayDeque<TypeMirror>(); |
754 |
types.add(type); |
755 |
while (!types.isEmpty()) { |
756 |
TypeMirror currentType = types.removeFirst(); |
757 |
if (currentType == null) { |
758 |
continue; |
759 |
} |
760 |
switch (currentType.getKind()) { |
761 |
case DECLARED: |
762 |
final Symbol typeSym = ((Type) currentType).tsym; |
763 |
if (typeSym != null && (typeSym.getKind().isClass() || typeSym.getKind().isInterface())) { |
764 |
addUsage(activeClass.peek(), typeSym, ClassIndexImpl.UsageType.TYPE_REFERENCE); |
765 |
} |
766 |
types.addAll(((DeclaredType) currentType).getTypeArguments()); |
767 |
break; |
768 |
case ARRAY: |
769 |
types.add(((ArrayType) currentType).getComponentType()); |
770 |
break; |
771 |
} |
772 |
} |
773 |
} |
774 |
} |