Skip to content

Handle wildcard imports in ChangeSdkType recipe #5336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public static String getV2Equivalent(String currentFqcn) {
return v2PackagePrefix + "." + v2ClassName;
}

public static String getV2ModelPackageWildCardEquivalent(String currentFqcn) {
int lastIndexOfDot = currentFqcn.lastIndexOf(".");
String packagePrefix = currentFqcn.substring(0, lastIndexOfDot);
String v2PackagePrefix = packagePrefix.replace(V1_PACKAGE_PREFIX, V2_PACKAGE_PREFIX);
return v2PackagePrefix + ".*";
}

private static String getV2ClientOrExceptionEquivalent(String className) {
if (className.startsWith("Abstract")) {
className = className.substring(8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package software.amazon.awssdk.migration.recipe;

import static software.amazon.awssdk.migration.internal.utils.NamingConversionUtils.getV2Equivalent;
import static software.amazon.awssdk.migration.internal.utils.NamingConversionUtils.getV2ModelPackageWildCardEquivalent;
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV1ClientClass;
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV1ModelClass;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -55,8 +58,9 @@
*/
@SdkInternalApi
public class ChangeSdkType extends Recipe {

private static final String V1_PATTERN = "com.amazonaws.services.";
private static final String V1_SERVICE_MODEL_WILD_CARD_CLASS_PATTERN =
"com\\.amazonaws\\.services\\.[a-zA-Z0-9]+\\.model\\.\\*";
private static final String V1_SERVICE_WILD_CARD_CLASS_PATTERN = "com\\.amazonaws\\.services\\.[a-zA-Z0-9]+\\.\\*";

@Override
public String getDisplayName() {
Expand All @@ -79,6 +83,7 @@ private static class ChangeTypeVisitor extends JavaVisitor<ExecutionContext> {

private final Map<JavaType, JavaType> oldNameToChangedType = new IdentityHashMap<>();
private final Set<String> topLevelClassnames = new HashSet<>();
private final List<String> wildcardImports = new ArrayList<>();

private Map<String, Pair<JavaType.Class, JavaType>> oldTypeToNewType = new HashMap<>();

Expand All @@ -99,19 +104,19 @@ public J visitImport(J.Import anImport, ExecutionContext ctx) {
.map(TypeUtils::asFullyQualified)
.orElse(null);

if (fullyQualified == null) {
if (fullyQualified == null) {
String fullName = anImport.getTypeName();
if (isWildcard(fullName)) {
maybeAddImport(getV2ModelPackageWildCardEquivalent(fullName));
wildcardImports.add(fullName);
}
return anImport;
}

String currentFqcn = fullyQualified.getFullyQualifiedName();

if (isV1ModelClass(fullyQualified) || isV1ClientClass(fullyQualified)) {
JavaType.ShallowClass originalType = JavaType.ShallowClass.build(currentFqcn);
String v2Equivalent = getV2Equivalent(currentFqcn);

JavaType targetType = JavaType.buildType(v2Equivalent);

oldTypeToNewType.put(currentFqcn, Pair.of(originalType, targetType));
if (isV1Class(fullyQualified)) {
storeV1ClassMetadata(currentFqcn);
if (anImport.getAlias() != null) {
importAlias = anImport.getAlias();
}
Expand All @@ -120,9 +125,13 @@ public J visitImport(J.Import anImport, ExecutionContext ctx) {
return anImport;
}

@Override
public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) {
return super.visitCompilationUnit(cu, executionContext);
private static boolean isWildcard(String fullName) {
return fullName.matches(V1_SERVICE_MODEL_WILD_CARD_CLASS_PATTERN) ||
fullName.matches(V1_SERVICE_WILD_CARD_CLASS_PATTERN);
}

private static boolean isV1Class(JavaType.FullyQualified fullyQualified) {
return isV1ModelClass(fullyQualified) || isV1ClientClass(fullyQualified);
}

@Override
Expand Down Expand Up @@ -230,6 +239,23 @@ private J postVisitSourceFile(JavaSourceFile tree, ExecutionContext ctx, J curre

currentTree = sourceFile;
}

return removeWildcardImports(ctx, currentTree, sourceFile);
}

private J removeWildcardImports(ExecutionContext ctx, J currentTree, JavaSourceFile sourceFile) {
for (String fqcn : wildcardImports) {
sourceFile = (JavaSourceFile) new RemoveImport<ExecutionContext>(fqcn)
.visit(sourceFile, ctx, getCursor().getParentOrThrow());

if (sourceFile != null) {
sourceFile = sourceFile.withImports(
ListUtils.map(sourceFile.getImports(), i -> visitAndCast(i, ctx,
super::visitImport)));
}

currentTree = sourceFile;
}
return currentTree;
}

Expand Down Expand Up @@ -281,33 +307,50 @@ public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) {
public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) {

JavaType currentType = ident.getType();
if (currentType instanceof JavaType.FullyQualified) {
JavaType.FullyQualified original = TypeUtils.asFullyQualified(currentType);
if (!(currentType instanceof JavaType.FullyQualified)) {
return visitAndCast(ident, ctx, super::visitIdentifier);
}

JavaType.FullyQualified original = TypeUtils.asFullyQualified(currentType);

if (original != null && TypeUtils.isOfClassType(ident.getType(), original.getFullyQualifiedName())) {
String fullyQualifiedName = original.getFullyQualifiedName();
if (original != null && TypeUtils.isOfClassType(ident.getType(), original.getFullyQualifiedName())) {
String fullyQualifiedName = original.getFullyQualifiedName();

if (oldTypeToNewType.containsKey(fullyQualifiedName)) {
JavaType.Class originalType = oldTypeToNewType.get(fullyQualifiedName).left();
String className = originalType.getClassName();
if (isV1Class(original)) {
storeV1ClassMetadata(fullyQualifiedName);
JavaType.Class originalType = oldTypeToNewType.get(fullyQualifiedName).left();
String className = originalType.getClassName();

if (ident.getSimpleName().equals(className)) {
JavaType targetType = oldTypeToNewType.get(fullyQualifiedName).right();
ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getClassName());
ident = ident.withType(updateType(currentType));
}
if (ident.getSimpleName().equals(className)) {
JavaType targetType = oldTypeToNewType.get(fullyQualifiedName).right();
ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getClassName());
ident = ident.withType(updateType(currentType));
}
}
}

return visitAndCast(ident, ctx, super::visitIdentifier);
}

private void storeV1ClassMetadata(String currentFqcn) {
JavaType.ShallowClass originalType = JavaType.ShallowClass.build(currentFqcn);
String v2Equivalent = getV2Equivalent(currentFqcn);

JavaType targetType = JavaType.buildType(v2Equivalent);

oldTypeToNewType.put(currentFqcn, Pair.of(originalType, targetType));
}

@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
for (Pair<JavaType.Class, JavaType> entry : oldTypeToNewType.values()) {
JavaType.Class originalType = entry.left();
JavaType targetType = entry.right();
JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
if (isV1Class(declaringType)) {
String fullyQualifiedName = declaringType.getFullyQualifiedName();
storeV1ClassMetadata(fullyQualifiedName);

Pair<JavaType.Class, JavaType> oldTypeToNewTypePair = oldTypeToNewType.get(fullyQualifiedName);
JavaType.Class originalType = oldTypeToNewTypePair.left();
JavaType targetType = oldTypeToNewTypePair.right();
if (method.getMethodType() != null && method.getMethodType().hasFlags(Flag.Static)) {
if (method.getMethodType().getDeclaringType().isAssignableFrom(originalType)) {
JavaSourceFile cu = getCursor().firstEnclosingOrThrow(JavaSourceFile.class);
Expand All @@ -329,6 +372,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx)
}
}
}

return super.visitMethodInvocation(method, ctx);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,12 @@ void v1Exception_shouldConvertToV2() {
assertThat(NamingConversionUtils.getV2Equivalent("com.amazonaws.services.iot.AmazonIOTException"))
.isEqualTo("software.amazon.awssdk.services.iot.IotException");
}

@Test
void v2WildCardImport_shouldConvertToV2() {
assertThat(NamingConversionUtils.getV2ModelPackageWildCardEquivalent("com.amazonaws.services.iot.model.*"))
.isEqualTo("software.amazon.awssdk.services.iot.model.*");
assertThat(NamingConversionUtils.getV2ModelPackageWildCardEquivalent("com.amazonaws.services.iot.*"))
.isEqualTo("software.amazon.awssdk.services.iot.*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@ void shouldChangeVariables() {
);
}

@Test
@EnabledOnJre({JRE.JAVA_8})
void wildCardImport_shouldRewrite() {
rewriteRun(
java(
"import com.amazonaws.services.sqs.model.*;\n" +
"import com.amazonaws.services.sqs.*;\n" +
"class Test {\n" +
" private DeleteQueueResult deleteQueResult;\n" +
" static void method(CreateQueueResult createQueueResult) {\n" +
" AmazonSQS sqs = null;\n" +
" ListQueuesRequest request = null;\n" +
" ListQueuesResult result = null;\n" +
" InvalidAttributeNameException exception = null;\n" +
" AmazonSQSException baseException = null;\n" +
" }\n" +
"}\n",
"import software.amazon.awssdk.services.sqs.*;\n"
+ "import software.amazon.awssdk.services.sqs.model.*;\n"
+ "\n"
+ "class Test {\n"
+ " private DeleteQueueResponse deleteQueResult;\n"
+ " static void method(CreateQueueResponse createQueueResult) {\n"
+ " SqsClient sqs = null;\n"
+ " ListQueuesRequest request = null;\n"
+ " ListQueuesResponse result = null;\n"
+ " InvalidAttributeNameException exception = null;\n"
+ " SqsException baseException = null;\n"
+ " }\n"
+ "}"
)
);
}

@Test
@EnabledOnJre({JRE.JAVA_8})
void shouldChangeFields() {
Expand Down
Loading