Stuck at adding dependency to Spring Indexer Context

This commit is contained in:
BoykoAlex
2022-02-10 12:06:59 -05:00
parent 14571e0c09
commit d9dce4722a
14 changed files with 636 additions and 320 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -12,13 +12,13 @@ package org.springframework.ide.vscode.boot.java.handlers;
import java.util.Collection;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
@@ -26,7 +26,7 @@ import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
public class AbstractSymbolProvider implements SymbolProvider {
@Override
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
public void addSymbols(Annotation node, FullyQualified typeBinding, Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
if (SCAN_PASS.ONE.equals(context.getPass())) {
addSymbolsPass1(node, typeBinding, metaAnnotations, context, doc);
}
@@ -36,7 +36,7 @@ public class AbstractSymbolProvider implements SymbolProvider {
}
@Override
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
public void addSymbols(ClassDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
if (SCAN_PASS.ONE.equals(context.getPass())) {
addSymbolsPass1(typeDeclaration, context, doc);
}
@@ -60,19 +60,19 @@ public class AbstractSymbolProvider implements SymbolProvider {
// implementations can decide whether to implement just pass1 or if they need 2 phases, they would have to implement both methods (pass1 + pass2)
//
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass1(Annotation node, FullyQualified typeBinding, Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
}
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass1(ClassDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
}
protected void addSymbolsPass1(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
}
protected void addSymbolsPass2(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass2(Annotation node, FullyQualified typeBinding, Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
}
protected void addSymbolsPass2(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass2(ClassDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
}
protected void addSymbolsPass2(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2021 Pivotal, Inc.
* Copyright (c) 2017, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -12,9 +12,9 @@ package org.springframework.ide.vscode.boot.java.handlers;
import java.util.List;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2019 Pivotal, Inc.
* Copyright (c) 2017, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -12,10 +12,10 @@ package org.springframework.ide.vscode.boot.java.handlers;
import java.util.Collection;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -25,8 +25,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
public interface SymbolProvider {
void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc);
void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc);
void addSymbols(Annotation node, FullyQualified typeBinding, Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc);
void addSymbols(ClassDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc);
void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 Pivotal, Inc.
* Copyright (c) 2017, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,16 +18,17 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.J.VariableDeclarations;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
@@ -35,6 +36,7 @@ import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping;
import org.springframework.ide.vscode.boot.java.livehover.v2.RequestMappingMetrics;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
@@ -58,8 +60,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
private static final int CODE_LENS_LIMIT = 3;
@Override
public Hover provideHover(ASTNode node, Annotation annotation,
ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
public Hover provideHover(J node, Annotation annotation,
int offset, TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return provideHover(annotation, doc, processLiveData);
}
@@ -69,7 +71,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
if (processLiveData.length > 0) {
List<Tuple2<LiveRequestMapping, SpringProcessLiveData>> val = getRequestMappingMethodFromRunningApp(annotation, processLiveData);
if (!val.isEmpty()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
org.openrewrite.marker.Range r = ORAstUtils.getRange(annotation);
Range hoverRange = doc.toRange(r.getStart().getOffset(), r.length());
return assembleCodeLenses(hoverRange, val);
}
}
@@ -175,7 +178,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
if (!val.isEmpty()) {
Hover hover = createHoverWithContent(val);
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
org.openrewrite.marker.Range r = ORAstUtils.getRange(annotation);
Range hoverRange = doc.toRange(r.getStart().getOffset(), r.length());
hover.setRange(hoverRange);
return hover;
} else {
@@ -220,21 +224,26 @@ public class RequestMappingHoverProvider implements HoverProvider {
rqClassName = rqClassName.replace('$', '.');
ASTNode parent = annotation.getParent();
J parent = ORAstUtils.getParent(annotation);
if (parent instanceof MethodDeclaration) {
MethodDeclaration methodDec = (MethodDeclaration) parent;
IMethodBinding binding = methodDec.resolveBinding();
if (binding != null) {
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
&& binding.getName().equals(rm.getMethodName())
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
.map(t -> t.getTypeDeclaration().getQualifiedName())
.toArray(String[]::new),
rm.getMethodParameters());
ClassDeclaration declaringClass = ORAstUtils.findNode(methodDec, ClassDeclaration.class);
if (declaringClass != null) {
FullyQualified type = declaringClass.getType();
return type != null && type.getFullyQualifiedName().equals(rqClassName)
&& methodDec.getSimpleName().equals(rm.getMethodName())
&& Arrays.equals(rm.getMethodParameters(),
methodDec.getParameters().stream()
.filter(VariableDeclarations.class::isInstance)
.map(VariableDeclarations.class::cast)
.map(v -> v.getTypeAsFullyQualified()) // what about primitive types, arrays etc???
.map(fq -> fq == null ? "" : fq.getFullyQualifiedName())
.toArray(String[]::new)
);
}
// } else if (parent instanceof TypeDeclaration) {
// TypeDeclaration typeDec = (TypeDeclaration) parent;
// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
} else if (parent instanceof ClassDeclaration) {
ClassDeclaration typeDec = (ClassDeclaration) parent;
return typeDec.getType() == null ? false : rqClassName.equals(typeDec.getType().getFullyQualifiedName());
}
}
return false;

View File

@@ -17,20 +17,20 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.Assignment;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -40,11 +40,12 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
@Override
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass1(Annotation node, FullyQualified annotationType, Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
if (node.getParent() instanceof MethodDeclaration) {
if (ORAstUtils.getParent(node) instanceof MethodDeclaration) {
try {
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
Range r = ORAstUtils.getRange(node);
Location location = new Location(doc.getUri(), doc.toRange(r.getStart().getOffset(), r.length()));
String[] path = getPath(node, context);
String[] parentPath = getParentPath(node, context);
String[] methods = getMethod(node, context);
@@ -69,9 +70,40 @@ public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
}
}
}
private String[] getRequestMethodFromMultiArgs(Annotation annotation, SpringIndexerJavaContext context) {
FullyQualified type = TypeUtils.asFullyQualified(annotation.getType());
if (type != null && Annotations.SPRING_REQUEST_MAPPING.equals(type.getFullyQualifiedName())) {
for (Expression arg : annotation.getArguments()) {
if (arg instanceof Assignment) {
Assignment assign = (Assignment) arg;
if ("method".equals(assign.getVariable().printTrimmed())) {
return ORAstUtils.getExpressionValueAsArray(assign.getAssignment(), context::addDependency);
}
}
}
}
return null;
}
private String[] getMethod(Annotation node, SpringIndexerJavaContext context) {
String[] methods = null;
List<Expression> args = node.getArguments();
// TODO: OR AST Annotation parameter
if (args == null || (args.size() == 1
&& (!(args.get(0) instanceof Assignment)) || "value".equals(((Assignment)args.get(0)).getVariable().printTrimmed()) )
) {
methods = getRequestMethod(node);
} else if () {
for (Expression arg : args) {
if (arg instanceof Assignment && "method".equals(((Assignment) arg).getVariable().printTrimmed())) {
}
}
}
if (node.isNormalAnnotation()) {
NormalAnnotation normNode = (NormalAnnotation) node;
@@ -156,10 +188,10 @@ public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
return null;
}
private String[] getRequestMethod(SingleMemberAnnotation annotation) {
ITypeBinding type = annotation.resolveTypeBinding();
private String[] getRequestMethod(Annotation annotation) {
FullyQualified type = TypeUtils.asFullyQualified(annotation.getType());
if (type != null) {
switch (type.getQualifiedName()) {
switch (type.getFullyQualifiedName()) {
case Annotations.SPRING_GET_MAPPING:
return new String[] { "GET" };
case Annotations.SPRING_POST_MAPPING:
@@ -170,6 +202,8 @@ public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
return new String[] { "PUT" };
case Annotations.SPRING_PATCH_MAPPING:
return new String[] { "PATCH" };
case Annotations.SPRING_REQUEST_MAPPING:
return new String[] { "GET" };
}
}
return null;

View File

@@ -12,16 +12,18 @@ package org.springframework.ide.vscode.boot.java.requestmapping;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -38,25 +40,23 @@ public class WebfluxHandlerCodeLensProvider implements CodeLensProvider {
@Override
public void provideCodeLenses(CancelChecker cancelToken, TextDocument document, CompilationUnit cu, List<CodeLens> resultAccumulator) {
cu.accept(new ASTVisitor() {
@Override
public boolean visit(MethodDeclaration node) {
provideCodeLens(cancelToken, node, document, resultAccumulator);
return super.visit(node);
}
});
new JavaIsoVisitor<List<CodeLens>>() {
public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, List<CodeLens> cl) {
provideCodeLens(cancelToken, method, document, cl);
return method;
};
}.visitNonNull(cu, resultAccumulator);
}
protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node, TextDocument document, List<CodeLens> resultAccumulator) {
protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration method, TextDocument document, List<CodeLens> resultAccumulator) {
cancelToken.checkCanceled();
IMethodBinding methodBinding = node.resolveBinding();
ClassDeclaration declaringType = ORAstUtils.findDeclaringType(method);
if (method != null && ORAstUtils.findDeclaringType(method) != null) {
if (methodBinding != null && methodBinding.getDeclaringClass() != null && methodBinding.getMethodDeclaration() != null
&& methodBinding.getDeclaringClass().getBinaryName() != null && methodBinding.getMethodDeclaration().toString() != null) {
final String handlerClass = methodBinding.getDeclaringClass().getBinaryName().trim();
final String handlerMethod = methodBinding.getMethodDeclaration().toString().trim();
final String handlerClass = declaringType.getType().getFullyQualifiedName();
final String handlerMethod = method.getMethodType().toString(); // TODO: OR AST likely a problem
cancelToken.checkCanceled();
@@ -75,7 +75,8 @@ public class WebfluxHandlerCodeLensProvider implements CodeLensProvider {
WebfluxHandlerInformation handlerInfo = (WebfluxHandlerInformation) object;
CodeLens codeLens = new CodeLens();
codeLens.setRange(document.toRange(node.getName().getStartPosition(), node.getName().getLength()));
Range r = ORAstUtils.getRange(method.getName());
codeLens.setRange(document.toRange(r.getStart().getOffset(), r.length()));
String httpMethod = WebfluxUtils.getStringRep(handlerInfo.getHttpMethods(), string -> string);
String codeLensCommand = httpMethod != null ? httpMethod + " " : "";

View File

@@ -10,24 +10,33 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.openrewrite.java.tree.J.Modifier;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Range;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -38,6 +47,24 @@ public class ASTUtils {
private static final Logger log = LoggerFactory.getLogger(ASTUtils.class);
public static DocumentRegion nameRegion(TextDocument doc, Annotation annotation) {
int start = annotation.getTypeName().getStartPosition();
int end = start + annotation.getTypeName().getLength();
if (doc.getSafeChar(start - 1) == '@') {
start--;
}
return new DocumentRegion(doc, start, end);
}
public static Optional<Range> nameRange(TextDocument doc, Annotation annotation) {
try {
return Optional.of(nameRegion(doc, annotation).asRange());
} catch (Exception e) {
log.error("", e);
return Optional.empty();
}
}
public static DocumentRegion stringRegion(TextDocument doc, StringLiteral node) {
DocumentRegion nodeRegion = nodeRegion(doc, node);
if (nodeRegion.startsWith("\"")) {
@@ -50,6 +77,109 @@ public class ASTUtils {
}
public static DocumentRegion nodeRegion(TextDocument doc, ASTNode node) {
int start = node.getStartPosition();
int end = start + node.getLength();
return new DocumentRegion(doc, start, end);
}
public static Optional<Expression> getAttribute(Annotation annotation, String name) {
if (annotation != null) {
try {
if (annotation.isSingleMemberAnnotation() && name.equals("value")) {
SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation;
return Optional.ofNullable(sma.getValue());
} else if (annotation.isNormalAnnotation()) {
NormalAnnotation na = (NormalAnnotation) annotation;
Object attributeObjs = na.getStructuralProperty(NormalAnnotation.VALUES_PROPERTY);
if (attributeObjs instanceof List) {
for (Object atrObj : (List<?>)attributeObjs) {
if (atrObj instanceof MemberValuePair) {
MemberValuePair mvPair = (MemberValuePair) atrObj;
if (name.equals(mvPair.getName().getIdentifier())) {
return Optional.ofNullable(mvPair.getValue());
}
}
}
}
}
} catch (Exception e) {
log.error("", e);
}
}
return Optional.empty();
}
/**
* For case where a expression can be either a String or a array of Strings and
* we are interested in the first element of the array. (I.e. typical case
* when annotation attribute is of type String[] (because Java allows using a single
* value as a convenient syntax for writing an array of length 1 in that case.
*/
public static Optional<String> getFirstString(Expression exp) {
if (exp instanceof StringLiteral) {
return Optional.ofNullable(getLiteralValue((StringLiteral) exp));
} else if (exp instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) exp;
Object objs = array.getStructuralProperty(ArrayInitializer.EXPRESSIONS_PROPERTY);
if (objs instanceof List) {
List<?> list = (List<?>) objs;
if (!list.isEmpty()) {
Object firstObj = list.get(0);
if (firstObj instanceof Expression) {
return getFirstString((Expression) firstObj);
}
}
}
}
return Optional.empty();
}
public static TypeDeclaration findDeclaringType(ASTNode node) {
while (node != null && !(node instanceof TypeDeclaration)) {
node = node.getParent();
}
return node != null ? (TypeDeclaration) node : null;
}
public static boolean hasExactlyOneConstructor(TypeDeclaration typeDecl) {
boolean oneFound = false;
MethodDeclaration[] methods = typeDecl.getMethods();
for (MethodDeclaration methodDeclaration : methods) {
if (methodDeclaration.isConstructor()) {
if (oneFound) {
return false;
} else {
oneFound = true;
}
}
}
return oneFound;
}
public static MethodDeclaration getAnnotatedMethod(Annotation annotation) {
ASTNode parent = annotation.getParent();
if (parent instanceof MethodDeclaration) {
return (MethodDeclaration)parent;
}
return null;
}
public static TypeDeclaration getAnnotatedType(Annotation annotation) {
ASTNode parent = annotation.getParent();
if (parent instanceof TypeDeclaration) {
return (TypeDeclaration)parent;
}
return null;
}
public static String getLiteralValue(StringLiteral node) {
synchronized (node.getAST()) {
return node.getLiteralValue();
}
}
public static String getExpressionValueAsString(Expression exp, Consumer<ITypeBinding> dependencies) {
if (exp instanceof StringLiteral) {
return getLiteralValue((StringLiteral) exp);
@@ -114,6 +244,20 @@ public class ASTUtils {
}
public static Collection<Annotation> getAnnotations(TypeDeclaration declaringType) {
Object modifiersObj = declaringType.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY);
if (modifiersObj instanceof List) {
ImmutableList.Builder<Annotation> annotations = ImmutableList.builder();
for (Object node : (List<?>)modifiersObj) {
if (node instanceof Annotation) {
annotations.add((Annotation) node);
}
}
return annotations.build();
}
return ImmutableList.of();
}
public static String getAnnotationType(Annotation annotation) {
ITypeBinding binding = annotation.resolveTypeBinding();
@@ -122,5 +266,41 @@ public class ASTUtils {
}
return null;
}
public static Optional<String> beanId(List<Object> modifiers) {
return modifiers.stream()
.filter(m -> m instanceof SingleMemberAnnotation)
.map(m -> (SingleMemberAnnotation) m)
.filter(m -> {
ITypeBinding typeBinding = m.resolveTypeBinding();
if (typeBinding != null) {
return Annotations.QUALIFIER.equals(typeBinding.getQualifiedName());
}
return false;
})
.findFirst()
.map(a -> a.getValue())
.filter(e -> e != null)
.map(e -> e.resolveConstantExpressionValue())
.filter(o -> o instanceof String)
.map(o -> (String) o);
}
public static Annotation getBeanAnnotation(MethodDeclaration method) {
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
ITypeBinding typeBinding = annotation.resolveTypeBinding();
if (typeBinding != null) {
String fqName = typeBinding.getQualifiedName();
if (Annotations.BEAN.equals(fqName)) {
return annotation;
}
}
}
}
return null;
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -10,10 +10,11 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.marker.Range;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
@@ -22,8 +23,9 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class DefaultSymbolProvider {
public static SymbolInformation provideDefaultSymbol(Annotation node, TextDocument doc) throws Exception {
Range r = ORAstUtils.getRange(node);
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
new Location(doc.getUri(), doc.toRange(r.getStart().getOffset(), r.length())));
return symbol;
}

View File

@@ -1,17 +1,34 @@
package org.springframework.ide.vscode.boot.java.utils;
import java.util.ArrayList;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.openrewrite.Cursor;
import org.openrewrite.SourceFile;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.Recipe;
import org.openrewrite.Result;
import org.openrewrite.Tree;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.UpdateSourcePositions;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Annotation;
@@ -19,6 +36,8 @@ import org.openrewrite.java.tree.J.Assignment;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.J.EnumValueSet;
import org.openrewrite.java.tree.J.FieldAccess;
import org.openrewrite.java.tree.J.Identifier;
import org.openrewrite.java.tree.J.Literal;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.J.NewArray;
@@ -37,41 +56,110 @@ public class ORAstUtils {
private static final Logger log = LoggerFactory.getLogger(ORAstUtils.class);
private static class AncestersMarker implements Marker {
private static class ParentMarker implements Marker {
private UUID uuid;
private List<J> ancesters = List.of();
private J parent;
public AncestersMarker(List<J> ancesters) {
public ParentMarker(J parent) {
this.uuid = Tree.randomId();
this.ancesters = ancesters;
this.parent = parent;
}
@Override
public UUID getId() {
return uuid;
}
@SuppressWarnings("unchecked")
public <T> T getFirstAnsector(Class<T> clazz) {
if (ancesters != null) {
for (J node : ancesters) {
if (clazz.isInstance(node)) {
return (T) node;
}
}
public J getParent() {
return parent;
}
public J getGrandParent() {
if (parent != null) {
return parent.getMarkers().findFirst(ParentMarker.class).map(m -> m.getParent()).orElse(null);
}
return null;
}
public J getParent() {
if (ancesters != null && !ancesters.isEmpty()) {
return ancesters.get(0);
public <T> T getFirstAnsector(Class<T> clazz) {
if (clazz.isInstance(parent)) {
return clazz.cast(parent);
} else if (parent != null) {
return parent.getMarkers().findFirst(ParentMarker.class).map(m -> m.getFirstAnsector(clazz)).orElse(null);
}
return null;
}
}
// private static class AncestersMarker implements Marker {
//
// private UUID uuid;
// private List<J> ancesters = List.of();
//
// public AncestersMarker(List<J> ancesters) {
// this.uuid = Tree.randomId();
// this.ancesters = ancesters;
// }
//
// @Override
// public UUID getId() {
// return uuid;
// }
//
// @SuppressWarnings("unchecked")
// public <T> T getFirstAnsector(Class<T> clazz) {
// if (ancesters != null) {
// for (J node : ancesters) {
// if (clazz.isInstance(node)) {
// return (T) node;
// }
// }
// }
// return null;
// }
//
// public J getParent() {
// if (ancesters != null && !ancesters.isEmpty()) {
// return ancesters.get(0);
// }
// return null;
// }
//
// public J getGrandParent() {
// if (ancesters != null && ancesters.size() > 1) {
// return ancesters.get(1);
// }
// return null;
// }
// }
private static class MarkParentRecipe extends Recipe {
@Override
public String getDisplayName() {
return "Create parent AST node references via markers";
}
@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<>() {
@Override
public @Nullable J visit(@Nullable Tree tree, ExecutionContext p) {
if (tree instanceof J) {
J j = (J) tree;
J parent = getCursor().getParent() == null ? null : getCursor().getParent().getValue();
J newJ = j.withMarkers(j.getMarkers().addIfAbsent(new ParentMarker(parent)));
super.visit(newJ, p);
return newJ;
}
return (J) tree;
}
};
}
}
public static J findAstNodeAt(CompilationUnit cu, int offset) {
AtomicReference<J> f = new AtomicReference<>();
new JavaIsoVisitor<AtomicReference<J>>() {
@@ -87,16 +175,18 @@ public class ORAstUtils {
&& offset <= range.getEnd().getOffset()) {
super.visit(tree, found);
if (found.get() == null) {
List<J> ancesters = new ArrayList<>();
for (Cursor c = getCursor(); c != null && !(c.getValue() instanceof SourceFile); c = c.getParent()) {
Object o = c.getValue();
if (o instanceof J) {
ancesters.add((J) o);
}
}
J n = node.withMarkers(node.getMarkers().addIfAbsent(new AncestersMarker(ancesters)));
found.set(n);
return n;
// List<J> ancesters = new ArrayList<>();
// for (Cursor c = getCursor(); c != null && !(c.getValue() instanceof SourceFile); c = c.getParent()) {
// Object o = c.getValue();
// if (o instanceof J) {
// ancesters.add((J) o);
// }
// }
// J n = node.withMarkers(node.getMarkers().addIfAbsent(new AncestersMarker(ancesters)));
// found.set(n);
// return n;
found.set(node);
return node;
}
} else {
return (J) tree;
@@ -113,13 +203,15 @@ public class ORAstUtils {
if (clazz.isInstance(node)) {
return (T) node;
}
AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
return ancestry.getFirstAnsector(clazz);
// AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
// return ancestry.getFirstAnsector(clazz);
return node.getMarkers().findFirst(ParentMarker.class).map(m -> m.getFirstAnsector(clazz)).orElse(null);
}
public static J getParent(J node) {
AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
return ancestry.getParent();
// AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
// return ancestry.getParent();
return node.getMarkers().findFirst(ParentMarker.class).map(m -> m.getParent()).orElse(null);
}
public static EnumValueSet getEnumValues(ClassDeclaration classDecl) {
@@ -293,5 +385,48 @@ public class ORAstUtils {
return null;
}
public static J getGrandParent(J j) {
return j.getMarkers().findFirst(ParentMarker.class).map(m -> m.getGrandParent()).orElse(null);
}
public static String[] getExpressionValueAsArray(Expression exp, Consumer<FullyQualified> dependencies) {
if (exp instanceof NewArray) {
NewArray array = (NewArray) exp;
return array.getInitializer().stream().map(e -> getExpressionValueAsString(e, dependencies))
.filter(Objects::nonNull).toArray(String[]::new);
} else {
String rm = getExpressionValueAsString(exp, dependencies);
if (rm != null) {
return new String[] { rm };
}
}
return null;
}
public static String getExpressionValueAsString(Expression exp, Consumer<FullyQualified> dependencies) {
// TODO: OR AST need to check if there is way to extract constant values from variables
if (exp instanceof Literal) {
return getLiteralValue((Literal) exp);
} else if (exp instanceof Identifier) {
Identifier id = (Identifier) exp;
return id.getSimpleName();
} else if (exp instanceof FieldAccess) {
FieldAccess fa = (FieldAccess) exp;
return getExpressionValueAsString(fa.getName(), dependencies);
} else {
return null;
}
}
public static List<CompilationUnit> parse(JavaParser parser, Iterable<Path> sourceFiles) {
List<CompilationUnit> cus = parser.parse(sourceFiles, null, new InMemoryExecutionContext());
List<Result> results = new UpdateSourcePositions().doNext(new MarkParentRecipe()).run(cus);
return results.stream().map(r -> r.getAfter() == null ? r.getBefore() : r.getAfter()).map(CompilationUnit.class::cast).collect(Collectors.toList());
}
public static List<CompilationUnit> parseInputs(JavaParser parser, Iterable<Parser.Input> inputs) {
List<CompilationUnit> cus = parser.parseInputs(inputs, null, new InMemoryExecutionContext());
List<Result> results = new UpdateSourcePositions().doNext(new MarkParentRecipe()).run(cus);
return results.stream().map(r -> r.getAfter() == null ? r.getBefore() : r.getAfter()).map(CompilationUnit.class::cast).collect(Collectors.toList());
}
}

View File

@@ -208,12 +208,10 @@ public class ORCompilationUnitCache implements DocumentContentProvider, Disposab
}
});
List<CompilationUnit> parseInputs = javaParser.parseInputs(List.of(input), null, new InMemoryExecutionContext());
Result result = new UpdateSourcePositions().run(parseInputs).get(0);
logger.info("CU Cache: created new AST for {}", uri.toString());
return (CompilationUnit) (result.getAfter() == null ? result.getBefore() : result.getAfter());
return ORAstUtils.parseInputs(javaParser, List.of(input)).get(0);
});
if (cu != null) {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,13 +11,12 @@
package org.springframework.ide.vscode.boot.java.utils;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.java.tree.TypeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
@@ -33,8 +32,8 @@ public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
private static final Logger log = LoggerFactory.getLogger(RestrictedDefaultSymbolProvider.class);
@Override
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding,
Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
protected void addSymbolsPass1(Annotation node, FullyQualified typeBinding,
Collection<FullyQualified> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
// provide default symbol only in case this annotation is not combined with @Bean annotation
if (!isCombinedWithAnnotation(node, Annotations.BEAN)) {
@@ -48,21 +47,15 @@ public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
}
private boolean isCombinedWithAnnotation(Annotation node, String annotation) {
ASTNode parent = node.getParent();
J parent = ORAstUtils.getParent(node);
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation anno = (Annotation) modifier;
IAnnotationBinding annotationBinding = anno.resolveAnnotationBinding();
String type = annotationBinding.getAnnotationType().getBinaryName();
if (type != null && type.equals(annotation)) {
return true;
}
for (Annotation a : method.getLeadingAnnotations()) {
FullyQualified otherAnnotationType = TypeUtils.asFullyQualified(a.getType());
if (otherAnnotationType != null && annotation.equals(otherAnnotationType.getFullyQualifiedName())) {
return true;
}
}
}

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
@@ -31,20 +32,19 @@ import java.util.stream.Stream;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FileASTRequestor;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.SymbolInformation;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaParser.Builder;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.java.tree.TypeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
@@ -191,22 +191,20 @@ public class SpringIndexerJava implements SpringIndexer {
}
private void scanFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception {
ASTParser parser = createParser(project, false);
JavaParser parser = createParser(project, false);
String docURI = updatedDoc.getDocURI();
long lastModified = updatedDoc.getLastModified();
Path path = new File(new URI(docURI)).toPath();
if (content == null) {
Path path = new File(new URI(docURI)).toPath();
content = new String(Files.readAllBytes(path));
}
String unitName = docURI.substring(docURI.lastIndexOf("/"));
parser.setUnitName(unitName);
log.debug("Scan file: {}", unitName);
parser.setSource(content.toCharArray());
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
byte[] contentBytes = content.getBytes();
CompilationUnit cu = ORAstUtils.parseInputs(parser, List.of(new Parser.Input(path, () -> new ByteArrayInputStream(contentBytes)))).get(0);
if (cu != null) {
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
@@ -232,7 +230,7 @@ public class SpringIndexerJava implements SpringIndexer {
}
private Set<String> scanFilesInternally(IJavaProject project, DocumentDescriptor[] docs) throws Exception {
ASTParser parser = createParser(project, false);
JavaParser parser = createParser(project, false);
// this is to keep track of already scanned files to avoid endless loops due to circular dependencies
Set<String> scannedTypes = new HashSet<>();
@@ -253,31 +251,30 @@ public class SpringIndexerJava implements SpringIndexer {
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
Multimap<String, String> dependencies = MultimapBuilder.hashKeys().hashSetValues().build();
FileASTRequestor requestor = new FileASTRequestor() {
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
File file = new File(sourceFilePath);
String docURI = UriUtil.toUri(file).toString();
DocumentDescriptor updatedDoc = updatedDocs.get(docURI);
long lastModified = updatedDoc.getLastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
lastModified, docRef, null, generatedSymbols, SCAN_PASS.ONE, new ArrayList<>());
scanAST(context);
dependencies.putAll(sourceFilePath, context.getDependencies());
scannedTypes.addAll(context.getScannedTypes());
fileScannedEvent(sourceFilePath);
}
};
parser.createASTs(javaFiles, null, new String[0], requestor, null);
Collection<Path> javaPaths = Arrays.stream(javaFiles).map(p -> Path.of(p)).collect(Collectors.toList());
List<CompilationUnit> cus = ORAstUtils.parse(parser, javaPaths);
for (CompilationUnit cu : cus) {
File file = cu.getSourcePath().toFile();
String sourceFilePath = file.getPath();
String docURI = UriUtil.toUri(file).toString();
DocumentDescriptor updatedDoc = updatedDocs.get(docURI);
long lastModifiedStamp = updatedDoc.getLastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
lastModifiedStamp, docRef, null, generatedSymbols, SCAN_PASS.ONE, new ArrayList<>());
scanAST(context);
dependencies.putAll(sourceFilePath, context.getDependencies());
scannedTypes.addAll(context.getScannedTypes());
fileScannedEvent(sourceFilePath);
}
for (CachedSymbol symbol : generatedSymbols) {
symbolHandler.addSymbol(project, symbol.getDocURI(), symbol.getEnhancedSymbol());
}
@@ -361,96 +358,69 @@ public class SpringIndexerJava implements SpringIndexer {
private String[] scanFiles(IJavaProject project, String[] javaFiles, List<CachedSymbol> generatedSymbols, SCAN_PASS pass)
throws Exception {
ASTParser parser = createParser(project, SCAN_PASS.ONE.equals(pass));
JavaParser parser = createParser(project, SCAN_PASS.ONE.equals(pass));
List<String> nextPassFiles = new ArrayList<>();
Collection<Path> javaPaths = Arrays.stream(javaFiles).map(p -> Path.of(p)).collect(Collectors.toList());
List<CompilationUnit> cus = ORAstUtils.parse(parser, javaPaths);
FileASTRequestor requestor = new FileASTRequestor() {
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
File file = new File(sourceFilePath);
String docURI = UriUtil.toUri(file).toString();
long lastModified = file.lastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
for (CompilationUnit cu : cus) {
File file = cu.getSourcePath().toFile();
String sourceFilePath = file.getPath();
String docURI = UriUtil.toUri(file).toString();
long lastModified = file.lastModified();
AtomicReference<TextDocument> docRef = new AtomicReference<>();
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
lastModified, docRef, null, generatedSymbols, pass, nextPassFiles);
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
lastModified, docRef, null, generatedSymbols, pass, nextPassFiles);
scanAST(context);
}
};
parser.createASTs(javaFiles, null, new String[0], requestor, null);
scanAST(context);
}
return (String[]) nextPassFiles.toArray(new String[nextPassFiles.size()]);
}
private void scanAST(final SpringIndexerJavaContext context) {
context.getCu().accept(new ASTVisitor() {
@Override
public boolean visit(TypeDeclaration node) {
new JavaIsoVisitor<SpringIndexerJavaContext>() {
public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, SpringIndexerJavaContext indexer) {
try {
context.addScannedType(node.resolveBinding());
extractSymbolInformation(node, context);
context.addScannedType(classDecl.getType());
extractSymbolInformation(classDecl, indexer);
}
catch (Exception e) {
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + classDecl.getSimpleName(), e);
}
return super.visit(node);
return super.visitClassDeclaration(classDecl, indexer);
}
@Override
public boolean visit(MethodDeclaration node) {
public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, SpringIndexerJavaContext indexer) {
try {
extractSymbolInformation(node, context);
extractSymbolInformation(method, indexer);
}
catch (Exception e) {
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + method.getSimpleName(), e);
}
return super.visit(node);
return super.visitMethodDeclaration(method, indexer);
}
@Override
public boolean visit(SingleMemberAnnotation node) {
public Annotation visitAnnotation(Annotation annotation, SpringIndexerJavaContext indexer) {
try {
extractSymbolInformation(node, context);
extractSymbolInformation(annotation, context);
}
catch (Exception e) {
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + annotation.printTrimmed(), e);
}
return super.visit(node);
return super.visitAnnotation(annotation, context);
}
@Override
public boolean visit(NormalAnnotation node) {
try {
extractSymbolInformation(node, context);
}
catch (Exception e) {
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
}
return super.visit(node);
}
@Override
public boolean visit(MarkerAnnotation node) {
try {
extractSymbolInformation(node, context);
}
catch (Exception e) {
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
}
return super.visit(node);
}
});
}.visitNonNull(context.getCu(), context);
dependencyTracker.update(context.getFile(), context.getDependencies());;
}
private void extractSymbolInformation(TypeDeclaration typeDeclaration, final SpringIndexerJavaContext context) throws Exception {
private void extractSymbolInformation(ClassDeclaration typeDeclaration, final SpringIndexerJavaContext context) throws Exception {
Collection<SymbolProvider> providers = symbolProviders.getAll();
if (!providers.isEmpty()) {
TextDocument doc = DocumentUtils.getTempTextDocument(context.getDocURI(), context.getDocRef(), context.getContent());
@@ -471,16 +441,16 @@ public class SpringIndexerJava implements SpringIndexer {
}
private void extractSymbolInformation(Annotation node, final SpringIndexerJavaContext context) throws Exception {
ITypeBinding typeBinding = node.resolveTypeBinding();
FullyQualified type = TypeUtils.asFullyQualified(node.getType());
if (typeBinding != null) {
Collection<SymbolProvider> providers = symbolProviders.get(typeBinding);
Collection<ITypeBinding> metaAnnotations = AnnotationHierarchies.getMetaAnnotations(typeBinding, symbolProviders::containsKey);
if (type != null) {
Collection<SymbolProvider> providers = symbolProviders.get(type);
Collection<FullyQualified> metaAnnotations = AnnotationHierarchies.getMetaAnnotations(type, symbolProviders::containsKey);
if (!providers.isEmpty()) {
TextDocument doc = DocumentUtils.getTempTextDocument(context.getDocURI(), context.getDocRef(), context.getContent());
for (SymbolProvider provider : providers) {
provider.addSymbols(node, typeBinding, metaAnnotations, context, doc);
provider.addSymbols(node, type, metaAnnotations, context, doc);
}
} else {
SymbolInformation symbol = provideDefaultSymbol(node, context);
@@ -497,9 +467,9 @@ public class SpringIndexerJava implements SpringIndexer {
private SymbolInformation provideDefaultSymbol(Annotation node, final SpringIndexerJavaContext context) {
try {
ITypeBinding type = node.resolveTypeBinding();
FullyQualified type = TypeUtils.asFullyQualified(node.getType());
if (type != null) {
String qualifiedName = type.getQualifiedName();
String qualifiedName = type.getFullyQualifiedName();
if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) {
TextDocument doc = DocumentUtils.getTempTextDocument(context.getDocURI(), context.getDocRef(), context.getContent());
return DefaultSymbolProvider.provideDefaultSymbol(node, doc);
@@ -513,31 +483,20 @@ public class SpringIndexerJava implements SpringIndexer {
return null;
}
private ASTParser createParser(IJavaProject project, boolean ignoreMethodBodies) throws Exception {
String[] classpathEntries = getClasspathEntries(project);
String[] sourceEntries = getSourceEntries(project);
ASTParser parser = ASTParser.newParser(AST.JLS16);
Map<String, String> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(JavaCore.VERSION_16, options);
parser.setCompilerOptions(options);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setStatementsRecovery(true);
parser.setBindingsRecovery(true);
parser.setResolveBindings(true);
parser.setIgnoreMethodBodies(ignoreMethodBodies);
parser.setEnvironment(classpathEntries, sourceEntries, null, false);
return parser;
private JavaParser createParser(IJavaProject project, boolean ignoreMethodBodies) throws Exception {
JavaParser jp = JavaParser.fromJavaVersion().build();
jp.setClasspath(getClasspathEntries(project));
return jp;
}
private String[] getClasspathEntries(IJavaProject project) throws Exception {
private Collection<Path> getClasspathEntries(IJavaProject project) throws Exception {
IClasspath classpath = project.getClasspath();
Stream<File> classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream();
return classpathEntries
.filter(file -> file.exists())
.map(file -> file.getAbsolutePath())
.toArray(String[]::new);
.map(file -> file.getAbsoluteFile())
.map(file -> file.toPath())
.collect(Collectors.toSet());
}
private String[] getSourceEntries(IJavaProject project) throws Exception {

View File

@@ -15,8 +15,9 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -123,9 +124,9 @@ public class SpringIndexerJavaContext {
return scannedTypes;
}
public void addScannedType(ITypeBinding scannedType) {
public void addScannedType(FullyQualified scannedType) {
if (scannedType != null) {
String type = scannedType.getKey();
String type = scannedType.getFullyQualifiedName();
scannedTypes.add(type);
dependencies.remove(type);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2019 Pivotal, Inc.
* Copyright (c) 2017, 2022 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,18 +18,16 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Annotation;
import org.openrewrite.java.tree.J.Assignment;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.Literal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
@@ -37,6 +35,7 @@ import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties;
import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperty;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData;
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Renderable;
@@ -59,16 +58,18 @@ public class ValueHoverProvider implements HoverProvider {
@Override
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc,
public Hover provideHover(J node, Annotation annotation, int offset, TextDocument doc,
IJavaProject project, SpringProcessLiveData[] processLiveData) {
try {
ASTNode foundNode = NodeFinder.perform(node, offset, 0);
ASTNode exactNode = getExactNode(foundNode);
if (node instanceof Literal) {
Literal literal = (Literal) node;
if (exactNode != null) {
return provideHover(exactNode.toString(), offset - exactNode.getStartPosition(),
exactNode.getStartPosition(), doc, processLiveData);
if (isProperLiteral(literal, annotation)) {
org.openrewrite.marker.Range r = ORAstUtils.getRange(literal);
return provideHover(literal.printTrimmed(), offset - r.getStart().getOffset(),
r.getStart().getOffset(), doc, processLiveData);
}
}
} catch (Exception e) {
logger.error("Error while generating live hovers for @Value", e);
@@ -77,23 +78,26 @@ public class ValueHoverProvider implements HoverProvider {
return null;
}
private ASTNode getExactNode(ASTNode exactNode) {
if (exactNode != null) {
// case: @Value("prefix<*>")
if (exactNode instanceof StringLiteral && exactNode.getParent() instanceof Annotation) {
if (exactNode.toString().startsWith("\"") && exactNode.toString().endsWith("\"")) {
return exactNode;
}
private boolean isProperLiteral(Literal l, Annotation a) {
// case: @Value("prefix<*>")
if (ORAstUtils.getParent(l) == a) {
String str = ORAstUtils.getLiteralValue(l);
if (str.startsWith("\"") && str.endsWith("\"")) {
return true;
}
// case: @Value(value="prefix<*>")
else if (exactNode instanceof StringLiteral && exactNode.getParent() instanceof MemberValuePair
&& "value".equals(((MemberValuePair) exactNode.getParent()).getName().toString())) {
if (exactNode.toString().startsWith("\"") && exactNode.toString().endsWith("\"")) {
return exactNode;
}
// case: @Value(value="prefix<*>")
else if (ORAstUtils.getParent(l) instanceof Assignment && ORAstUtils.getGrandParent(l) == a) {
Assignment assign = (Assignment) ORAstUtils.getParent(l);
if ("value".equals(assign.getVariable().printTrimmed())) {
String str = ORAstUtils.getLiteralValue(l);
if (str.startsWith("\"") && str.endsWith("\"")) {
return true;
}
}
}
return null;
return false;
}
private Hover provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringProcessLiveData[] processLiveData) {
@@ -153,10 +157,7 @@ public class ValueHoverProvider implements HoverProvider {
* @param processLiveData
* @return
*/
private List<CodeLens> provideHighlightHints(TextDocument doc, StringLiteral node, SpringProcessLiveData[] processLiveData) {
ASTNode exactNode = getExactNode(node);
if (exactNode != null) {
private List<CodeLens> provideHighlightHints(TextDocument doc, Literal l, SpringProcessLiveData[] processLiveData) {
Map<String, List<LocalRange>> propertiesWithRanges = new HashMap<>();
// Get the escaped value that INCLUDES the quotes as we want to compute
@@ -166,7 +167,7 @@ public class ValueHoverProvider implements HoverProvider {
// to highlight a.prop we need to take into account the starting '"' after the
// '='
// to get the correct range of a.prop
String nodeValue = node.getEscapedValue();
String nodeValue = ORAstUtils.getLiteralValue(l);
if (nodeValue != null) {
// Get property names with ranges highlight
propertiesWithRanges = parseProperties(nodeValue);
@@ -188,7 +189,8 @@ public class ValueHoverProvider implements HoverProvider {
propRanges.stream().forEach(propRange -> {
try {
Range hoverRange = doc.toRange(exactNode.getStartPosition() + propRange.getStart(),
org.openrewrite.marker.Range r = ORAstUtils.getRange(l);
Range hoverRange = doc.toRange(r.getStart().getOffset() + propRange.getStart(),
propRange.getEnd() - propRange.getStart());
lenses.add(new CodeLens(hoverRange));
} catch (BadLocationException e) {
@@ -202,7 +204,6 @@ public class ValueHoverProvider implements HoverProvider {
}
return lenses;
}
}
return ImmutableList.of();
}
@@ -360,7 +361,7 @@ public class ValueHoverProvider implements HoverProvider {
}
@Override
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
public Hover provideHover(J node, ClassDeclaration typeDeclaration, int offset,
TextDocument doc, IJavaProject project, SpringProcessLiveData[] processLiveData) {
return null;
}
@@ -370,15 +371,18 @@ public class ValueHoverProvider implements HoverProvider {
// Show highlight hints for properties in @Value that have live information
List<CodeLens> lenses = new ArrayList<>();
annotation.accept(new ASTVisitor() {
@Override
public boolean visit(StringLiteral node) {
List<CodeLens> provideHighlightHints = provideHighlightHints(doc, node, processLiveData);
lenses.addAll(provideHighlightHints);
return super.visit(node);
}
});
new JavaIsoVisitor<List<CodeLens>>() {
public Literal visitLiteral(Literal literal, List<CodeLens> cl) {
if (isProperLiteral(literal, annotation)) {
cl.addAll(provideHighlightHints(doc, literal, processLiveData));
}
return super.visitLiteral(literal, cl);
};
}.visitNonNull(annotation, lenses);
return lenses;
}