diff --git a/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java b/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java new file mode 100644 index 00000000000..d53fa550e7a --- /dev/null +++ b/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java @@ -0,0 +1,66 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.Nullable; +import org.openrewrite.trait.Reference; + +import java.util.*; + +@Incubating(since = "8.39.0") +public interface SourceFileWithReferences extends SourceFile { + + References getReferences(); + + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + @Getter + class References { + private final SourceFile sourceFile; + private final Set references; + + public Collection findMatches(Reference.Matcher matcher) { + return findMatchesInternal(matcher, null); + } + + public Collection findMatches(Reference.Matcher matcher, Reference.Kind kind) { + return findMatchesInternal(matcher, kind); + } + + private List findMatchesInternal(Reference.Matcher matcher, Reference.@Nullable Kind kind) { + List list = new ArrayList<>(); + for (Reference ref : references) { + if ((kind == null || ref.getKind().equals(kind)) && ref.matches(matcher) ) { + list.add(ref); + } + } + return list; + } + + public static References build(SourceFile sourceFile) { + Set references = new HashSet<>(); + ServiceLoader loader = ServiceLoader.load(Reference.Provider.class); + loader.forEach(provider -> { + if (provider.isAcceptable(sourceFile)) { + references.addAll(provider.getReferences(sourceFile)); + } + }); + return new References(sourceFile, references); + } + } +} diff --git a/rewrite-core/src/main/java/org/openrewrite/SourceFileWithTypeReferences.java b/rewrite-core/src/main/java/org/openrewrite/SourceFileWithTypeReferences.java deleted file mode 100644 index e4f8974a02b..00000000000 --- a/rewrite-core/src/main/java/org/openrewrite/SourceFileWithTypeReferences.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openrewrite; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.openrewrite.trait.TypeReference; - -import java.util.*; - -@Incubating(since = "8.39.0") -public interface SourceFileWithTypeReferences extends SourceFile { - - TypeReferences getTypeReferences(); - - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - @Getter - class TypeReferences { - private final SourceFile sourceFile; - private final Set typeReferences; - - public Collection findMatches(TypeReference.Matcher matcher) { - List list = new ArrayList<>(); - for (TypeReference ref : typeReferences) { - if (ref.matches(matcher)) { - list.add(ref); - } - } - return list; - } - - public static TypeReferences build(SourceFile sourceFile) { - Set typeReferences = new HashSet<>(); - ServiceLoader loader = ServiceLoader.load(TypeReference.Provider.class); - loader.forEach(provider -> { - if (provider.isAcceptable(sourceFile)) { - typeReferences.addAll(provider.getTypeReferences(sourceFile)); - } - }); - return new TypeReferences(sourceFile, typeReferences); - } - } -} diff --git a/rewrite-core/src/main/java/org/openrewrite/TypeReferenceProvider.java b/rewrite-core/src/main/java/org/openrewrite/TypeReferenceProvider.java deleted file mode 100644 index b89ea2c767e..00000000000 --- a/rewrite-core/src/main/java/org/openrewrite/TypeReferenceProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openrewrite; - -import org.openrewrite.trait.TypeReference; - -import java.util.Set; - -public interface TypeReferenceProvider { - - Set getTypeReferences(SourceFile sourceFile); - - boolean isAcceptable(SourceFile sourceFile); -} diff --git a/rewrite-core/src/main/java/org/openrewrite/trait/TypeReference.java b/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java similarity index 55% rename from rewrite-core/src/main/java/org/openrewrite/trait/TypeReference.java rename to rewrite-core/src/main/java/org/openrewrite/trait/Reference.java index 8d2e89140e6..dbaea758e6f 100644 --- a/rewrite-core/src/main/java/org/openrewrite/trait/TypeReference.java +++ b/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java @@ -15,29 +15,48 @@ */ package org.openrewrite.trait; -import org.openrewrite.Incubating; -import org.openrewrite.SourceFile; -import org.openrewrite.Tree; +import org.openrewrite.*; import java.util.Set; @Incubating(since = "8.39.0") -public interface TypeReference extends Trait { +public interface Reference extends Trait { - String getName(); + enum Kind { + TYPE, + PACKAGE + } + + Kind getKind(); + + String getValue(); + + default boolean supportsRename() { + return false; + } default boolean matches(Matcher matcher) { - return matcher.matchesName(getName()); + return matcher.matchesReference(this); + } + + default TreeVisitor rename(Renamer renamer, String replacement) { + return renamer.rename(replacement); } interface Provider { - Set getTypeReferences(SourceFile sourceFile); + Set getReferences(SourceFile sourceFile); boolean isAcceptable(SourceFile sourceFile); } interface Matcher { - boolean matchesName(String name); + boolean matchesReference(Reference value); + } + + interface Renamer { + default TreeVisitor rename(String replacement) { + throw new UnsupportedOperationException(); + } } } diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangePackageTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangePackageTest.java index f21841e0a08..c307f330f27 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangePackageTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangePackageTest.java @@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.xml.Assertions.xml; @SuppressWarnings("ConstantConditions") class ChangePackageTest implements RewriteTest { @@ -1702,4 +1703,26 @@ public enum MyEnum { ) ); } + + @Test + void changePackageInSpringXml() { + rewriteRun( + spec -> spec.recipe(new ChangePackage("test.type", "test.test.type", true)), + xml( + """ + + + + + """, + """ + + + + + """ + ) + ); + + } } diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java index b2234b43a47..752814a7d40 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java @@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.xml.Assertions.xml; @SuppressWarnings("ConstantConditions") class ChangeTypeTest implements RewriteTest { @@ -2019,4 +2020,26 @@ class Letters { ); } + @Test + void changeTypeInSpringXml() { + rewriteRun( + spec -> spec.recipe(new ChangeType("test.type.A", "test.type.B", true)), + xml( + """ + + + + + """, + """ + + + + + """ + ) + ); + + } + } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java index 7d28b412ca0..876410cc5cf 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java @@ -23,8 +23,10 @@ import org.openrewrite.internal.ListUtils; import org.openrewrite.java.tree.*; import org.openrewrite.marker.SearchResult; +import org.openrewrite.trait.Reference; import java.nio.file.Paths; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; @@ -83,11 +85,12 @@ public Validated validate() { @Override public TreeVisitor getVisitor() { - JavaIsoVisitor condition = new JavaIsoVisitor() { + TreeVisitor condition = new TreeVisitor() { @Override - public @Nullable J preVisit(J tree, ExecutionContext ctx) { + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + stopAfterPreVisit(); if (tree instanceof JavaSourceFile) { - JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); + JavaSourceFile cu = (JavaSourceFile) tree; if (cu.getPackageDeclaration() != null) { String original = cu.getPackageDeclaration().getExpression() .printTrimmed(getCursor()).replaceAll("\\s", ""); @@ -111,16 +114,48 @@ public TreeVisitor getVisitor() { } } } - stopAfterPreVisit(); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences cu = (SourceFileWithReferences) tree; + boolean recursive = Boolean.TRUE.equals(ChangePackage.this.recursive); + String recursivePackageNamePrefix = oldPackageName + "."; + for (Reference ref : cu.getReferences().getReferences()) { + if (ref.getValue().equals(oldPackageName) || recursive && ref.getValue().startsWith(recursivePackageNamePrefix)) { + return SearchResult.found(cu); + } + } } - return super.preVisit(tree, ctx); + return tree; } }; - return Preconditions.check(condition, new ChangePackageVisitor()); + return Preconditions.check(condition, new TreeVisitor() { + @Override + public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { + return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithReferences; + } + + @Override + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + stopAfterPreVisit(); + if (tree instanceof JavaSourceFile) { + return new JavaChangePackageVisitor().visit(tree, ctx, requireNonNull(getCursor().getParent())); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; + SourceFileWithReferences.References references = sourceFile.getReferences(); + boolean recursive = Boolean.TRUE.equals(ChangePackage.this.recursive); + PackageMatcher matcher = new PackageMatcher(oldPackageName, recursive); + Map matches = new HashMap<>(); + for (Reference ref : references.findMatches(matcher)) { + matches.put(ref.getTree(), ref); + } + return new ReferenceChangePackageVisitor(matches, matcher, newPackageName).visit(tree, ctx, requireNonNull(getCursor().getParent())); + } + return tree; + } + }); } - private class ChangePackageVisitor extends JavaVisitor { + private class JavaChangePackageVisitor extends JavaVisitor { private static final String RENAME_TO_KEY = "renameTo"; private static final String RENAME_FROM_KEY = "renameFrom"; @@ -349,4 +384,22 @@ private boolean isTargetRecursivePackageName(String packageName) { } } + + @Value + @EqualsAndHashCode(callSuper = false) + private static class ReferenceChangePackageVisitor extends TreeVisitor { + Map matches; + Reference.Renamer renamer; + String newPackageName; + + @Override + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + Reference reference = matches.get(tree); + if (reference != null && reference.supportsRename()) { + return reference.rename(renamer, newPackageName).visit(tree, ctx, getCursor().getParent()); + } + return tree; + } + } + } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java index b3ce211b049..04dd93a0c99 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java @@ -24,6 +24,7 @@ import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import org.openrewrite.marker.SearchResult; +import org.openrewrite.trait.Reference; import java.nio.file.Path; import java.nio.file.Paths; @@ -81,22 +82,49 @@ public String getDescription() { public TreeVisitor getVisitor() { TreeVisitor condition = new TreeVisitor() { @Override - public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + stopAfterPreVisit(); if (tree instanceof JavaSourceFile) { - JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); + JavaSourceFile cu = (JavaSourceFile) tree; if (!Boolean.TRUE.equals(ignoreDefinition) && containsClassDefinition(cu, oldFullyQualifiedTypeName)) { return SearchResult.found(cu); } return new UsesType<>(oldFullyQualifiedTypeName, true).visitNonNull(cu, ctx); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences cu = (SourceFileWithReferences) tree; + return new UsesType<>(oldFullyQualifiedTypeName, true).visitNonNull(cu, ctx); } return tree; } }; - return Preconditions.check(condition, new ChangeTypeVisitor(oldFullyQualifiedTypeName, newFullyQualifiedTypeName, ignoreDefinition)); + return Preconditions.check(condition, new TreeVisitor() { + @Override + public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { + return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithReferences; + } + + @Override + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + stopAfterPreVisit(); + if (tree instanceof JavaSourceFile) { + return new JavaChangeTypeVisitor(oldFullyQualifiedTypeName, newFullyQualifiedTypeName, ignoreDefinition).visit(tree, ctx, requireNonNull(getCursor().getParent())); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; + SourceFileWithReferences.References references = sourceFile.getReferences(); + TypeMatcher matcher = new TypeMatcher(oldFullyQualifiedTypeName); + Map matches = new HashMap<>(); + for (Reference ref : references.findMatches(matcher, Reference.Kind.TYPE)) { + matches.put(ref.getTree(), ref); + } + return new ReferenceChangeTypeVisitor(matches, matcher, newFullyQualifiedTypeName).visit(tree, ctx, requireNonNull(getCursor().getParent())); + } + return tree; + } + }); } - private static class ChangeTypeVisitor extends JavaVisitor { + private static class JavaChangeTypeVisitor extends JavaVisitor { private final JavaType.Class originalType; private final JavaType targetType; @@ -109,7 +137,7 @@ private static class ChangeTypeVisitor extends JavaVisitor { private final Map oldNameToChangedType = new IdentityHashMap<>(); private final Set topLevelClassnames = new HashSet<>(); - private ChangeTypeVisitor(String oldFullyQualifiedTypeName, String newFullyQualifiedTypeName, @Nullable Boolean ignoreDefinition) { + private JavaChangeTypeVisitor(String oldFullyQualifiedTypeName, String newFullyQualifiedTypeName, @Nullable Boolean ignoreDefinition) { this.originalType = JavaType.ShallowClass.build(oldFullyQualifiedTypeName); this.targetType = JavaType.buildType(newFullyQualifiedTypeName); this.ignoreDefinition = ignoreDefinition; @@ -527,6 +555,23 @@ private boolean hasNoConflictingImport(@Nullable JavaSourceFile sf) { } } + @Value + @EqualsAndHashCode(callSuper = false) + private static class ReferenceChangeTypeVisitor extends TreeVisitor { + Map matches; + Reference.Renamer renamer; + String newFullyQualifiedName; + + @Override + public @Nullable Tree preVisit(@Nullable Tree tree, ExecutionContext ctx) { + Reference reference = matches.get(tree); + if (reference != null && reference.supportsRename()) { + return reference.rename(renamer, newFullyQualifiedName).visit(tree, ctx, getCursor().getParent()); + } + return tree; + } + } + private static class ChangeClassDefinition extends JavaIsoVisitor { private final JavaType.Class originalType; private final JavaType.Class targetType; @@ -579,7 +624,7 @@ private boolean updatePath(JavaSourceFile sf, String oldPath, String newPath) { } @Override - public J.@Nullable Package visitPackage(J.Package pkg, ExecutionContext ctx) { + public J.@Nullable Package visitPackage(J.Package pkg, ExecutionContext ctx) { Boolean updatePackage = getCursor().pollNearestMessage("UPDATE_PACKAGE"); if (updatePackage != null && updatePackage) { String original = pkg.getExpression().printTrimmed(getCursor()).replaceAll("\\s", ""); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/PackageMatcher.java b/rewrite-java/src/main/java/org/openrewrite/java/PackageMatcher.java new file mode 100644 index 00000000000..c6a88019001 --- /dev/null +++ b/rewrite-java/src/main/java/org/openrewrite/java/PackageMatcher.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java; + +import lombok.Getter; +import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.StringUtils; +import org.openrewrite.trait.Reference; +import org.openrewrite.xml.tree.Xml; + +@Getter +public class PackageMatcher implements Reference.Renamer, Reference.Matcher { + + private final @Nullable String targetPackage; + + @Getter + private final Boolean recursive; + + public PackageMatcher(@Nullable String targetPackage) { + this(targetPackage, false); + } + + public PackageMatcher(@Nullable String targetPackage, boolean recursive) { + this.targetPackage = targetPackage; + this.recursive = recursive; + } + + @Override + public boolean matchesReference(Reference reference) { + if (reference.getKind().equals(Reference.Kind.TYPE) || reference.getKind().equals(Reference.Kind.PACKAGE)) { + String recursivePackageNamePrefix = targetPackage + "."; + if (reference.getValue().equals(targetPackage) || recursive && reference.getValue().startsWith(recursivePackageNamePrefix)) { + return true; + } + } + return false; + } + + @Override + public TreeVisitor rename(String newValue) { + return new TreeVisitor() { + @Override + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + if (StringUtils.isNotEmpty(newValue)) { + if (tree instanceof Xml.Attribute) { + return ((Xml.Attribute) tree).withValue(((Xml.Attribute) tree).getValue().withValue(getReplacement(((Xml.Attribute) tree).getValueAsString(), targetPackage, newValue))); + } + if (tree instanceof Xml.Tag) { + if (((Xml.Tag) tree).getValue().isPresent()) { + return ((Xml.Tag) tree).withValue(getReplacement(((Xml.Tag) tree).getValue().get(), targetPackage, newValue)); + } + } + } + return super.visit(tree, ctx); + } + }; + } + + String getReplacement(String value, @Nullable String oldValue, String newValue) { + if (oldValue != null) { + if (recursive) { + return value.replace(oldValue, newValue); + } else if (value.startsWith(oldValue) && Character.isUpperCase(value.charAt(oldValue.length() + 1))) { + return value.replace(oldValue, newValue); + } + } + return value; + } + +} diff --git a/rewrite-java/src/main/java/org/openrewrite/java/TypeMatcher.java b/rewrite-java/src/main/java/org/openrewrite/java/TypeMatcher.java index 66149adb535..fe5a8cdea9f 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/TypeMatcher.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/TypeMatcher.java @@ -19,6 +19,9 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; import org.openrewrite.internal.StringUtils; import org.openrewrite.java.internal.grammar.MethodSignatureLexer; import org.openrewrite.java.internal.grammar.MethodSignatureParser; @@ -26,14 +29,15 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeTree; import org.openrewrite.java.tree.TypeUtils; -import org.openrewrite.trait.TypeReference; +import org.openrewrite.trait.Reference; +import org.openrewrite.xml.tree.Xml; import java.util.regex.Pattern; import static org.openrewrite.java.tree.TypeUtils.fullyQualifiedNamesAreEqual; @Getter -public class TypeMatcher implements TypeReference.Matcher { +public class TypeMatcher implements Reference.Renamer, Reference.Matcher { private static final String ASPECTJ_DOT_PATTERN = StringUtils.aspectjNameToPattern("."); @SuppressWarnings("NotNullFieldNotInitialized") @@ -112,7 +116,26 @@ private static boolean isPlainIdentifier(MethodSignatureParser.TargetTypePattern } @Override - public boolean matchesName(String name) { - return matchesTargetTypeName(name); + public boolean matchesReference(Reference reference) { + return reference.getKind().equals(Reference.Kind.TYPE) && matchesTargetTypeName(reference.getValue()); } + + @Override + public TreeVisitor rename(String newValue) { + return new TreeVisitor() { + @Override + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + if (StringUtils.isNotEmpty(newValue)) { + if (tree instanceof Xml.Attribute) { + return ((Xml.Attribute) tree).withValue(((Xml.Attribute) tree).getValue().withValue(newValue)); + } + if (tree instanceof Xml.Tag) { + return ((Xml.Tag) tree).withValue(newValue); + } + } + return super.visit(tree, ctx); + } + }; + } + } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java b/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java index 97a468dbffa..9c936327614 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java @@ -28,6 +28,7 @@ import org.openrewrite.java.tree.*; import org.openrewrite.marker.SearchResult; import org.openrewrite.trait.Trait; +import org.openrewrite.trait.Reference; import java.util.HashSet; import java.util.Set; @@ -68,19 +69,19 @@ public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>(fullyQualifiedTypeName, false), new TreeVisitor() { @Override public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { - return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithTypeReferences; + return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithReferences; } @Override public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { return new JavaSourceFileVisitor(fullyQualifiedType).visit(tree, ctx); - } else if (tree instanceof SourceFileWithTypeReferences) { - SourceFileWithTypeReferences sourceFile = (SourceFileWithTypeReferences) tree; - SourceFileWithTypeReferences.TypeReferences typeReferences = sourceFile.getTypeReferences(); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; + SourceFileWithReferences.References references = sourceFile.getReferences(); TypeMatcher matcher = new TypeMatcher(fullyQualifiedTypeName); - Set matches = typeReferences.findMatches(matcher).stream().map(Trait::getTree).collect(Collectors.toSet()); - return new TypeReferenceVisitor(matches).visit(tree, ctx); + Set matches = references.findMatches(matcher, Reference.Kind.TYPE).stream().map(Trait::getTree).collect(Collectors.toSet()); + return new ReferenceVisitor(matches).visit(tree, ctx); } return tree; } @@ -149,7 +150,7 @@ private static boolean typeMatches(boolean checkAssignability, Pattern pattern, @Value @EqualsAndHashCode(callSuper = false) - private static class TypeReferenceVisitor extends TreeVisitor { + private static class ReferenceVisitor extends TreeVisitor { Set matches; @Override diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java index 3d348fc8cbe..2e701f37491 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java @@ -18,7 +18,7 @@ import lombok.Getter; import org.jspecify.annotations.Nullable; import org.openrewrite.SourceFile; -import org.openrewrite.SourceFileWithTypeReferences; +import org.openrewrite.SourceFileWithReferences; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.StringUtils; @@ -28,7 +28,7 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; import org.openrewrite.marker.SearchResult; -import org.openrewrite.trait.TypeReference; +import org.openrewrite.trait.Reference; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -77,7 +77,7 @@ public UsesType(String fullyQualifiedType, @Nullable Boolean includeImplicit) { @Override public boolean isAcceptable(SourceFile sourceFile, P p) { - return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithTypeReferences; + return sourceFile instanceof JavaSourceFile || sourceFile instanceof SourceFileWithReferences; } @Override @@ -119,11 +119,11 @@ public boolean isAcceptable(SourceFile sourceFile, P p) { } } } - } else if (tree instanceof SourceFileWithTypeReferences) { - SourceFileWithTypeReferences sourceFile = (SourceFileWithTypeReferences) tree; - SourceFileWithTypeReferences.TypeReferences typeReferences = sourceFile.getTypeReferences(); + } else if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; + SourceFileWithReferences.References references = sourceFile.getReferences(); TypeMatcher matcher = typeMatcher != null ? typeMatcher : new TypeMatcher(fullyQualifiedType); - for (TypeReference ignored : typeReferences.findMatches(matcher)) { + for (Reference ignored : references.findMatches(matcher, Reference.Kind.TYPE)) { return SearchResult.found(sourceFile); } } diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringTypeReference.java b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java similarity index 61% rename from rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringTypeReference.java rename to rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java index edc9bdc6e83..9b52ae44d42 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringTypeReference.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java @@ -17,29 +17,34 @@ import lombok.Value; import org.jspecify.annotations.Nullable; -import org.openrewrite.Cursor; -import org.openrewrite.SourceFile; -import org.openrewrite.Tree; +import org.openrewrite.*; import org.openrewrite.trait.SimpleTraitMatcher; -import org.openrewrite.trait.TypeReference; +import org.openrewrite.trait.Reference; import org.openrewrite.xml.XPathMatcher; import org.openrewrite.xml.tree.Xml; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; @Value -class SpringTypeReference implements TypeReference { +class SpringReference implements Reference { Cursor cursor; + Kind kind; @Override public Tree getTree() { - return TypeReference.super.getTree(); + return Reference.super.getTree(); } @Override - public String getName() { + public Kind getKind() { + return kind; + } + + @Override + public String getValue() { if (getTree() instanceof Xml.Attribute) { Xml.Attribute attribute = (Xml.Attribute) getTree(); return attribute.getValueAsString(); @@ -52,45 +57,58 @@ public String getName() { throw new IllegalArgumentException("getTree() must be an Xml.Attribute or Xml.Tag: " + getTree().getClass()); } - static class Matcher extends SimpleTraitMatcher { - private final Pattern typeReference = Pattern.compile("(?:[a-zA-Z_][a-zA-Z0-9_]*\\.)+[A-Z*][a-zA-Z0-9_]*(?:<[a-zA-Z0-9_,?<> ]*>)?"); + @Override + public boolean supportsRename() { + return true; + } + + static class Matcher extends SimpleTraitMatcher { + private final Pattern referencePattern = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*"); private final XPathMatcher classXPath = new XPathMatcher("//@class"); private final XPathMatcher typeXPath = new XPathMatcher("//@type"); + private final XPathMatcher keyTypeXPath = new XPathMatcher("//@key-type"); + private final XPathMatcher valueTypeXPath = new XPathMatcher("//@value-type"); private final XPathMatcher tags = new XPathMatcher("//value"); @Override - protected @Nullable SpringTypeReference test(Cursor cursor) { + protected @Nullable SpringReference test(Cursor cursor) { Object value = cursor.getValue(); if (value instanceof Xml.Attribute) { Xml.Attribute attrib = (Xml.Attribute) value; - if (classXPath.matches(cursor) || typeXPath.matches(cursor)) { - if (typeReference.matcher(attrib.getValueAsString()).matches()) { - return new SpringTypeReference(cursor); + if (classXPath.matches(cursor) || typeXPath.matches(cursor) || keyTypeXPath.matches(cursor) || valueTypeXPath.matches(cursor)) { + String stringVal = attrib.getValueAsString(); + if (referencePattern.matcher(stringVal).matches()) { + return new SpringReference(cursor, determineKind(stringVal)); } } } else if (value instanceof Xml.Tag) { Xml.Tag tag = (Xml.Tag) value; if (tags.matches(cursor)) { - if (tag.getValue().isPresent() && typeReference.matcher(tag.getValue().get()).matches()) { - return new SpringTypeReference(cursor); + Optional stringVal = tag.getValue(); + if (stringVal.isPresent() && referencePattern.matcher(stringVal.get()).matches()) { + return new SpringReference(cursor, determineKind(stringVal.get())); } } } return null; } + + Kind determineKind(String value) { + return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; + } } @SuppressWarnings("unused") - public static class Provider implements TypeReference.Provider { + public static class Provider implements Reference.Provider { @Override - public Set getTypeReferences(SourceFile sourceFile) { - Set typeReferences = new HashSet<>(); + public Set getReferences(SourceFile sourceFile) { + Set references = new HashSet<>(); new Matcher().asVisitor(reference -> { - typeReferences.add(reference); + references.add(reference); return reference.getTree(); }).visit(sourceFile, 0); - return typeReferences; + return references; } @Override diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java b/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java index 4e0eb50db4d..1fabf24388c 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java @@ -83,7 +83,7 @@ default

boolean isAcceptable(TreeVisitor v, P p) { @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) @RequiredArgsConstructor @AllArgsConstructor(access = AccessLevel.PRIVATE) - class Document implements Xml, SourceFileWithTypeReferences { + class Document implements Xml, SourceFileWithReferences { @With @EqualsAndHashCode.Include UUID id; @@ -164,25 +164,25 @@ public T service(Class service) { if (WhitespaceValidationService.class.getName().equals(service.getName())) { return (T) new XmlWhitespaceValidationService(); } - return SourceFileWithTypeReferences.super.service(service); + return SourceFileWithReferences.super.service(service); } @Nullable @NonFinal - transient SoftReference typeReferences; + transient SoftReference references; @Transient @Override - public TypeReferences getTypeReferences() { - TypeReferences cache; - if (this.typeReferences == null) { - cache = TypeReferences.build(this); - this.typeReferences = new SoftReference<>(cache); + public References getReferences() { + References cache; + if (this.references == null) { + cache = References.build(this); + this.references = new SoftReference<>(cache); } else { - cache = this.typeReferences.get(); + cache = this.references.get(); if (cache == null || cache.getSourceFile() != this) { - cache = TypeReferences.build(this); - this.typeReferences = new SoftReference<>(cache); + cache = References.build(this); + this.references = new SoftReference<>(cache); } } return cache; diff --git a/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider b/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider new file mode 100644 index 00000000000..72d76d75ee4 --- /dev/null +++ b/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider @@ -0,0 +1 @@ +org.openrewrite.xml.trait.SpringReference$Provider \ No newline at end of file diff --git a/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.TypeReference$Provider b/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.TypeReference$Provider deleted file mode 100644 index 6f58258f8bc..00000000000 --- a/rewrite-xml/src/main/resources/META-INF/services/org.openrewrite.trait.TypeReference$Provider +++ /dev/null @@ -1 +0,0 @@ -org.openrewrite.xml.trait.SpringTypeReference$Provider \ No newline at end of file diff --git a/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java b/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java index 7eda3abcda9..90bb94ff45f 100755 --- a/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java +++ b/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java @@ -24,6 +24,7 @@ import org.openrewrite.Issue; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.trait.Reference; import org.openrewrite.xml.tree.Xml; import java.nio.file.Paths; @@ -120,7 +121,7 @@ void parseXmlDocument() { } @Test - void javaTypeReferenceDocument() { + void javaReferenceDocument() { rewriteRun( xml( """ @@ -135,13 +136,18 @@ void javaTypeReferenceDocument() { java.lang.String + + java.lang + """, spec -> spec.afterRecipe(doc -> { - assertThat(doc.getTypeReferences().getTypeReferences().stream().anyMatch(typeRef -> typeRef.getName().equals("java.lang.String"))); + assertThat(doc.getReferences().getReferences().stream().anyMatch(typeRef -> typeRef.getValue().equals("java.lang.String"))); + assertThat(doc.getReferences().getReferences().stream().anyMatch(typeRef -> typeRef.getKind().equals(Reference.Kind.TYPE))); + assertThat(doc.getReferences().getReferences().stream().anyMatch(typeRef -> typeRef.getKind().equals(Reference.Kind.PACKAGE))); }) ) ); diff --git a/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringTypeReferenceTest.java b/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringReferenceTest.java similarity index 78% rename from rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringTypeReferenceTest.java rename to rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringReferenceTest.java index 5259faf3cf5..d60f5a2e8a9 100644 --- a/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringTypeReferenceTest.java +++ b/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringReferenceTest.java @@ -23,12 +23,12 @@ import static org.openrewrite.xml.Assertions.xml; -class SpringTypeReferenceTest implements RewriteTest { +class SpringReferenceTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(RewriteTest.toRecipe(() -> new SpringTypeReference.Matcher() - .asVisitor(springJavaTypeReference -> SearchResult.found(springJavaTypeReference.getTree(), springJavaTypeReference.getName())))); + spec.recipe(RewriteTest.toRecipe(() -> new SpringReference.Matcher() + .asVisitor(springJavaTypeReference -> SearchResult.found(springJavaTypeReference.getTree(), springJavaTypeReference.getValue())))); } @SuppressWarnings("SpringXmlModelInspection") @@ -50,6 +50,12 @@ void xmlConfiguration() { java.lang.String + + java.lang + + + + @@ -68,6 +74,12 @@ void xmlConfiguration() { java.lang.String + + java.lang + + + -->key-type="java.lang.String" value-type="java.lang.String"/> +