-
Notifications
You must be signed in to change notification settings - Fork 365
Replies: 1 comment · 6 replies
-
For the first question have a look at this utility class: rewrite/rewrite-core/src/main/java/org/openrewrite/internal/ReflectionUtils.java Lines 31 to 50 in 804dea0
And are you sure you want to create the interface in the same compilation unit? Otherwise you might want switch to a ScanningRecipe and the generate methods in there. |
Beta Was this translation helpful? Give feedback.
All reactions
-
I have completed writing my ScanningRecipe. In getScanner, I can correctly obtain the interfaces I need to add, but these interfaces are all from second-party libraries. My getVisitor doesn't reach the printing logic and import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import org.apache.commons.collections.CollectionUtils;
import org.openrewrite.*;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@EqualsAndHashCode(callSuper = false)
@Value
public class CreateInherited4FeignClientRecipe extends ScanningRecipe<Set<CreateInherited4FeignClientRecipe.InterfaceInfoNeedToBeCreate>> {
@Option(displayName = "Exclude prefix",
description = "The exclude prefix of the new interface file.",
example = "[com.xxx.claim]")
List<String> excludePrefix;
@JsonCreator
public CreateInherited4FeignClientRecipe(
@JsonProperty("excludePrefix") List<String> excludePrefix
) {
this.excludePrefix = excludePrefix;
}
private static final String AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired";
private static final String FQN_QUALIFIER = "org.springframework.beans.factory.annotation.Qualifier";
private static final String FEIGN_CLIENT = "org.springframework.cloud.openfeign.FeignClient";
private static final String OLD_FEIGN_CLIENT = "org.springframework.cloud.netflix.feign.FeignClient";
private static final String REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";
@JsonIgnore
private static final String NEW_CLASS_TEMPLATE = """
package %s;
import %s;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = "%s", url = "%s", path = "%s")
public interface %s extends %s {
}
""";
@Override
public Set<InterfaceInfoNeedToBeCreate> getInitialValue(ExecutionContext ctx) {
return new HashSet<>();
}
@Override
public TreeVisitor<?, ExecutionContext> getScanner(Set<InterfaceInfoNeedToBeCreate> acc) {
return Preconditions.check(precondition(), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext executionContext) {
multiVariable = super.visitVariableDeclarations(multiVariable, executionContext);
J.Annotation autowired = multiVariable.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), AUTOWIRED))
.findFirst().orElse(null);
J.Annotation qualifier = multiVariable.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), FQN_QUALIFIER))
.findFirst().orElse(null);
if (autowired == null && qualifier == null) {
return multiVariable;
}
JavaType.FullyQualified variableType = (JavaType.FullyQualified) multiVariable.getType();
if (variableType == null || TypeUtils.isAssignableTo("java.util.Collection", variableType) || TypeUtils.isAssignableTo("java.util.Map", variableType) || CollectionUtils.exists(excludePrefix, prefix -> variableType.getFullyQualifiedName().startsWith((String) prefix))) {
return multiVariable;
}
if (variableType.getAnnotations().isEmpty()) {
return multiVariable;
}
InterfaceInfoNeedToBeCreate i = new InterfaceInfoNeedToBeCreate(variableType);
acc.add(i);
return multiVariable;
}
});
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor(Set<InterfaceInfoNeedToBeCreate> acc) {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
J.CompilationUnit newCu = super.visitCompilationUnit(cu, ctx);
Map<String, J.ClassDeclaration> classes = newCu.getClasses()
.stream()
.filter(cd -> cd.getType() != null)
.collect(Collectors.toMap(cd -> cd.getType().getFullyQualifiedName(), Function.identity()));
InterfaceInfoNeedToBeCreate interfaceInfoNeedToBeCreate = acc.stream().filter(i -> classes.containsKey(i.getExtendedInterface().getFullyQualifiedName())).findFirst().orElse(null);
if (interfaceInfoNeedToBeCreate == null) {
return newCu;
}
JavaType.FullyQualified feignClientType = interfaceInfoNeedToBeCreate.getExtendedInterface();
J.ClassDeclaration classDecal = classes.get(feignClientType.getFullyQualifiedName());
J.Annotation requestMapping = classDecal.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), REQUEST_MAPPING))
.findFirst().orElse(null);
J.Annotation feignClient = classDecal.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), FEIGN_CLIENT))
.findFirst().orElse(null);
if (feignClient == null) {
return newCu;
}
interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName = excludePrefix.get(0) + ".remote." + feignClientType.getClassName();
interfaceInfoNeedToBeCreate.feignPath = extractAttributeFromRequestMapping(feignClient, "path");
interfaceInfoNeedToBeCreate.feignName = extractAttributeFromRequestMapping(feignClient, "name");
interfaceInfoNeedToBeCreate.feignUrl = requestMapping == null ? "" : extractAttributeFromRequestMapping(requestMapping, "value");
System.out.println(interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName + "________________" + feignClientType.getFullyQualifiedName());
doAfterVisit(new ChangeType(interfaceInfoNeedToBeCreate.extendedInterface.getFullyQualifiedName(), interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName, true)
.getVisitor());
return newCu;
}
};
}
@Override
public Collection<? extends SourceFile> generate(Set<InterfaceInfoNeedToBeCreate> acc, ExecutionContext ctx) {
List<JavaSourceFile> newInterface = List.of();
acc.forEach(interfaceInfoNeedToBeCreate -> {
String packageName = interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName.substring(0, interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName.lastIndexOf("."));
String interfaceName = interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName.substring(interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName.lastIndexOf(".") + 1);
String originalInterfaceFullyQualifiedName = interfaceInfoNeedToBeCreate.extendedInterface.getFullyQualifiedName();
String originalInterfaceName = interfaceInfoNeedToBeCreate.extendedInterface.getClassName();
newInterface.add((JavaSourceFile) JavaParser.fromJavaVersion().build()
.parse(String.format(NEW_CLASS_TEMPLATE, packageName, originalInterfaceFullyQualifiedName, interfaceInfoNeedToBeCreate.feignName, interfaceInfoNeedToBeCreate.feignUrl, interfaceInfoNeedToBeCreate.feignPath, interfaceName, originalInterfaceName))
.map(brandNewFile -> brandNewFile.withSourcePath(getSourcePath(interfaceInfoNeedToBeCreate.interfaceFullyQualifiedInterfaceName)))
);
});
return newInterface;
}
@Override
public @NlsRewrite.DisplayName String getDisplayName() {
return "Create interface for feign client";
}
@Override
public @NlsRewrite.Description String getDescription() {
return "Replace second-party package's FeignClient with new interface.";
}
private String extractAttributeFromRequestMapping(J.Annotation annotation, String attributeName) {
List<Expression> args = annotation.getArguments();
if (args == null || args.isEmpty()) {
return "";
}
String value = "";
for (Expression arg : args) {
value = getAttributeValue(arg, attributeName);
if (value != null && !value.isEmpty()){
return value;
}
}
return "";
}
private Path getSourcePath(String fullyQualifiedName) {
return Paths.get(String.format(
"%s/%s.java",
"src/main/java",
fullyQualifiedName.replace('.', '/')));
}
private TreeVisitor<?, ExecutionContext> precondition() {
return Preconditions.or(
new UsesType<>(FQN_QUALIFIER, false),
new UsesType<>(AUTOWIRED, false));
}
private String getAttributeValue(Expression arg, String attributeName) {
if (arg instanceof J.Literal) {
J.Literal literal = (J.Literal) arg;
return (String) literal.getValue();
} else if (arg instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) arg;
if (assignment.getVariable() instanceof J.Identifier) {
J.Identifier variable = (J.Identifier) assignment.getVariable();
if (attributeName.equals(variable.getSimpleName())) {
Expression expression = assignment.getAssignment();
if (expression instanceof J.Literal) {
J.Literal value = (J.Literal) expression;
return (String) value.getValue();
}
}
}
} else if (arg instanceof J.NewArray) {
List<Expression> initializer = ((J.NewArray) arg).getInitializer();
if (initializer != null && initializer.size() == 1) {
return getAttributeValue(initializer.get(0), attributeName);
}
}
return null;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class InterfaceInfoNeedToBeCreate {
String interfaceFullyQualifiedInterfaceName ;
String feignPath ;
String feignName ;
String feignUrl ;
JavaType.FullyQualified extendedInterface;
public InterfaceInfoNeedToBeCreate(JavaType.FullyQualified extendedInterface) {
this.extendedInterface = extendedInterface;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
InterfaceInfoNeedToBeCreate that = (InterfaceInfoNeedToBeCreate) o;
return Objects.equals(extendedInterface.getFullyQualifiedName(), that.extendedInterface.getFullyQualifiedName());
}
@Override
public int hashCode() {
return Objects.hash(extendedInterface.getFullyQualifiedName());
}
}
} |
Beta Was this translation helpful? Give feedback.
All reactions
-
Types from dependencies are pulled in when parsing your project. We only visit sources that are part of your project, not any types from dependencies. If those types are missing look for problems reported when parsing your project, and/or use the diagnostic recipes listed here: |
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Really appreciate your help!!! I've got the information of types from dependencies thought my customize classloader. However, i can not get annotation information yet. Are there any way for me to extend and set annotation info for javaType? |
Beta Was this translation helpful? Give feedback.
All reactions
-
I want to get attributes of FeignClient like name,url,path... |
Beta Was this translation helpful? Give feedback.
All reactions
-
Why the custom classloader? That might complicate things. We only officially support the Maven and gradle plugins to run recipes, anything beyond that is not likely to work well due to missing markers and type information |
Beta Was this translation helpful? Give feedback.
-
BackGround:I want to create a new FeignClient interface that inherits from a second-party package's FeignClient and migrate its attributes
my code is as below:
my questions are:
Beta Was this translation helpful? Give feedback.
All reactions