diff --git a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/SampleDelegateCommandHandler.java b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/SampleDelegateCommandHandler.java index 37e9036..7681de9 100644 --- a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/SampleDelegateCommandHandler.java +++ b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/SampleDelegateCommandHandler.java @@ -162,7 +162,7 @@ private static SearchPattern mapLocationToSearchPatternLocation(int location, St */ private static SearchPattern getPatternSingleQuery(int location, String query) throws Exception { var pattern = SearchPattern.R_PATTERN_MATCH; - if ((!query.contains("?") || !query.contains("*")) && (location != 11)) { + if ((!query.contains("?") && !query.contains("*")) && (location != 11)) { logInfo("Using full match"); pattern = SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE; } @@ -376,7 +376,8 @@ private static List search(String projectName, ArrayList> ACCEPTED_CLASSES = new ArrayList<>(); + static { + ACCEPTED_CLASSES.add(ResolvedSourceMethod.class); + ACCEPTED_CLASSES.add(ResolvedSourceField.class); + ACCEPTED_CLASSES.add(ResolvedSourceType.class); + } @Override public List get(SearchMatch match) throws CoreException { List symbols = new ArrayList<>(); try { - IAnnotatable mod = (IAnnotatable) match.getElement(); - IJavaElement element = (IJavaElement) match.getElement(); - for (IAnnotation annotation : mod.getAnnotations()) { + IAnnotatable annotatable = (IAnnotatable) match.getElement(); + for (IAnnotation annotation : annotatable.getAnnotations()) { + IJavaElement annotationElement = annotation.getPrimaryElement(); SymbolInformation symbol = new SymbolInformation(); symbol.setName(annotation.getElementName()); - symbol.setKind(convertSymbolKind(element)); + symbol.setKind(convertSymbolKind(annotationElement)); symbol.setContainerName(annotation.getParent().getElementName()); - symbol.setLocation(getLocation(element, match)); - - if (annotationQuery != null) { - List> classes = new ArrayList<>(); - classes.add(ResolvedSourceMethod.class); - classes.add(ResolvedSourceField.class); - classes.add(ResolvedSourceType.class); - if (matchesAnnotationQuery(match, classes)) { - symbols.add(symbol); + Location location = getLocation(annotationElement, match); + symbol.setLocation(location); + if (this.query.contains(".")) { + // First try to get compilation unit for source files + ICompilationUnit unit = (ICompilationUnit) annotationElement.getAncestor(IJavaElement.COMPILATION_UNIT); + if (unit == null) { + // If not in source, try to get class file for compiled classes + IClassFile cls = (IClassFile) annotationElement.getAncestor(IJavaElement.CLASS_FILE); + if (cls != null) { + unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null); + } + } + if (unit != null) { + IType t = unit.getType(annotationElement.getElementName()); + String fqdn = ""; + if (!t.isResolved()) { + var elements = unit.codeSelect(match.getOffset(), match.getLength()); + for (IJavaElement e: Arrays.asList(elements)) { + if (e instanceof IType) { + var newT = (IType) e; + if (newT.isResolved()) { + fqdn = newT.getFullyQualifiedName('.'); + logInfo("FQDN from code select: " + fqdn); + } + } + } + } else { + fqdn = t.getFullyQualifiedName('.'); + logInfo("resolved type: " + fqdn); + } + if (query.matches(fqdn) || fqdn.matches(query)) { + if (unit.isWorkingCopy()) { + unit.discardWorkingCopy(); + unit.close(); + } + + if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) { + symbols.add(symbol); + } + return symbols; + } + } + + logInfo("falling back to resolving via AST"); + + if (this.queryQualificationMatches(this.query.replaceAll("\\(([A-Za-z_][A-Za-z0-9_]*(\\|[A-Za-z_][A-Za-z0-9_]*)*)\\)", ".*"), annotationElement, unit, location)) { + ASTParser astParser = ASTParser.newParser(AST.getJLSLatest()); + astParser.setSource(unit); + astParser.setResolveBindings(true); + CompilationUnit cu = (CompilationUnit) astParser.createAST(null); + CustomASTVisitor visitor = new CustomASTVisitor(query, match, QueryLocation.ANNOTATION); + // Under tests, resolveConstructorBinding will return null if there are problems + IProblem[] problems = cu.getProblems(); + if (problems != null && problems.length > 0) { + logInfo("KONVEYOR_LOG: " + "Found " + problems.length + " problems while compiling"); + int count = 0; + for (IProblem problem : problems) { + logInfo("KONVEYOR_LOG: Problem - ID: " + problem.getID() + " Message: " + problem.getMessage()); + count++; + if (count >= SymbolProvider.MAX_PROBLEMS_TO_LOG) { + logInfo("KONVEYOR_LOG: Only showing first " + SymbolProvider.MAX_PROBLEMS_TO_LOG + " problems, " + (problems.length - SymbolProvider.MAX_PROBLEMS_TO_LOG) + " more not displayed"); + break; + } + } + } + cu.accept(visitor); + if (visitor.symbolMatches()) { + if (annotationQuery != null) { + if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) { + symbols.add(symbol); + } + } else { + symbols.add(symbol); + } + } + } + if (unit != null && unit.isWorkingCopy()) { + unit.discardWorkingCopy(); + unit.close(); } } else { - symbols.add(symbol); + if (annotationQuery != null) { + if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) { + symbols.add(symbol); + } + } else { + symbols.add(symbol); + } } } return symbols; @@ -60,4 +155,9 @@ public AnnotationQuery getAnnotationQuery() { public void setAnnotationQuery(AnnotationQuery annotationQuery) { this.annotationQuery = annotationQuery; } + + @Override + public void setQuery(String query) { + this.query = query; + } } diff --git a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/ConstructorCallSymbolProvider.java b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/ConstructorCallSymbolProvider.java index 8b19b3d..4f4561c 100644 --- a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/ConstructorCallSymbolProvider.java +++ b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/ConstructorCallSymbolProvider.java @@ -53,7 +53,7 @@ public List get(SearchMatch match) throws CoreException { unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null); } } - if (this.queryQualificationMatches(this.query, unit, location)) { + if (this.queryQualificationMatches(this.query, mod, unit, location)) { ASTParser astParser = ASTParser.newParser(AST.getJLSLatest()); astParser.setSource(unit); astParser.setResolveBindings(true); diff --git a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/CustomASTVisitor.java b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/CustomASTVisitor.java index 2d06786..f53459e 100644 --- a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/CustomASTVisitor.java +++ b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/CustomASTVisitor.java @@ -6,9 +6,14 @@ import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.ConstructorInvocation; +import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.search.SearchMatch; /* @@ -32,6 +37,7 @@ public class CustomASTVisitor extends ASTVisitor { public enum QueryLocation { METHOD_CALL, CONSTRUCTOR_CALL, + ANNOTATION, } public CustomASTVisitor(String query, SearchMatch match, QueryLocation location) { @@ -62,6 +68,64 @@ private boolean shouldVisit(ASTNode node) { (node.getStartPosition() + node.getLength()); } + @Override + public boolean visit(MarkerAnnotation node) { + return visit((Annotation) node); + } + + @Override + public boolean visit(NormalAnnotation node) { + return visit((Annotation) node); + } + + @Override + public boolean visit(SingleMemberAnnotation node) { + return visit((Annotation) node); + } + + private boolean visit(Annotation node) { + // There is a problem with trying to run shouldVisit() here because + // matches on annotations aren't directly on the annotation node, + // but on the annotated one (class, method, field, etc). So we can't + // use shouldVisit() to filter out nodes we don't want to visit. + // TODO: think of a better way to handle this + if (this.location != QueryLocation.ANNOTATION) { + return true; + } + try { + IAnnotationBinding binding = node.resolveAnnotationBinding(); + if (binding != null) { + // get fqn + ITypeBinding declaringClass = binding.getAnnotationType(); + if (declaringClass != null) { + // Handle Erasure results + if (declaringClass.getErasure() != null) { + declaringClass = declaringClass.getErasure(); + } + String fullyQualifiedName = declaringClass.getQualifiedName(); + // match fqn with query pattern + if (fullyQualifiedName.matches(this.query)) { + this.symbolMatches = true; + return false; + } else { + logInfo("method fqn " + fullyQualifiedName + " did not match with " + query); + return true; + } + } + } + logInfo("failed to get accurate info for MethodInvocation, falling back"); + // sometimes binding or declaring class cannot be found, usually due to errors + // in source code. in that case, we will fallback and accept the match + this.symbolMatches = true; + return false; + } catch (Exception e) { + logInfo("KONVEYOR_LOG: error visiting MethodInvocation node: " + e); + // this is so that we fallback and don't lose a match when we fail + this.symbolMatches = true; + return false; + } + } + /* * This is to get information from a MethodInvocation, used for METHOD_CALL * we discard a match only when we can tell for sure. otherwise we accept diff --git a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/MethodCallSymbolProvider.java b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/MethodCallSymbolProvider.java index 257554d..8826eda 100644 --- a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/MethodCallSymbolProvider.java +++ b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/MethodCallSymbolProvider.java @@ -53,7 +53,7 @@ public List get(SearchMatch match) { unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null); } } - if (this.queryQualificationMatches(this.query, unit, location)) { + if (this.queryQualificationMatches(this.query, e, unit, location)) { ASTParser astParser = ASTParser.newParser(AST.getJLSLatest()); astParser.setSource(unit); astParser.setResolveBindings(true); diff --git a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/SymbolProvider.java b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/SymbolProvider.java index 79b0115..b86a5d5 100644 --- a/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/SymbolProvider.java +++ b/java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/SymbolProvider.java @@ -188,7 +188,7 @@ private static void setPosition(Position position, int[] coords) { * 3. the compilation unit has a package declaration as `konveyor.io.Util` * we do this so that we can rule out a lot of matches before going the AST route */ - default boolean queryQualificationMatches(String query, ICompilationUnit unit, Location location) { + default boolean queryQualificationMatches(String query, IJavaElement matchedElement,ICompilationUnit unit, Location location) { // Make sure that the ICompilationUnit is conistant try { unit.makeConsistent(null); @@ -206,6 +206,14 @@ default boolean queryQualificationMatches(String query, ICompilationUnit unit, L // for a query, java.io.paths.File*, queryQualification is java.io.paths queryQualification = query.substring(0, dotIndex); } + // an element need not be imported if its referenced by fqn + if (!queryQualification.isEmpty() && ( + matchedElement.getElementName().equals(queryQualification) + || matchedElement.getElementName().startsWith(queryQualification + ".") + || queryQualification.matches(matchedElement.getElementName()) + )) { + return true; + } String packageQueryQualification = ""; int packageDotIndex = queryQualification.lastIndexOf('.'); if (packageDotIndex > 0) { diff --git a/java-analyzer-bundle.tp/java-analyzer-bundle.target b/java-analyzer-bundle.tp/java-analyzer-bundle.target index 80032b4..219276e 100644 --- a/java-analyzer-bundle.tp/java-analyzer-bundle.target +++ b/java-analyzer-bundle.tp/java-analyzer-bundle.target @@ -2,12 +2,12 @@ - - + @@ -64,7 +64,7 @@ - +