From 6904c9b60b62a65c6ac1b8089ac2efc4fada2fda Mon Sep 17 00:00:00 2001 From: Jacob van Lingen Date: Tue, 17 Dec 2024 12:38:02 +0100 Subject: [PATCH] Improve preconditions check (#120) * Starting point: PreConditionsVerifier * Starting point: PreConditionsVerifier * Starting point: PreConditionsVerifier * Starting point: PreConditionsVerifier (failing tests as intended) * Starting point: PreConditionsVerifier (failing tests as intended) * Fix unit test * Implementation * Implementation * improvement * improvement * Fix test * Add extra info about pruning * Start new implementation, introduce PreCondition objects * Start with PreConditionTest * Start with PreConditionTest * improvement * toString improvement * toString improvement * Rename `PreCondition` to `Precondition` + add copyright header * Improve `fitsInto` * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Revert formatting * Revert formatting * Revert formatting * Revert formatting * Reorder imports in PreConditionsVerifier.java * Change name `PreConditionsVerifier` => `PreconditionsVerifier` * Change name `PreConditionsVerifier` => `PreconditionsVerifier` * Improvement: extractCommonElements * Update src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java Co-authored-by: Tim te Beek * Change test accordingly * Revert test * Remove unneeded `RequiredArgsConstructor` * Apply suggestions from code review Co-authored-by: Tim te Beek * Apply suggestions from code review --------- Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../java/template/processor/Precondition.java | 193 ++++++ .../processor/RefasterTemplateProcessor.java | 61 +- .../RefasterTemplateProcessorTest.java | 1 + .../template/processor/PreconditionTest.java | 205 +++++++ .../resources/refaster/EscapesRecipes.java | 4 +- .../refaster/MethodThrowsRecipe.java | 2 +- .../refaster/PreconditionsVerifier.java | 147 +++++ .../PreconditionsVerifierRecipes.java | 560 ++++++++++++++++++ .../refaster/RefasterAnyOfRecipes.java | 12 +- .../refaster/TwoVisitMethodsRecipe.java | 4 +- 10 files changed, 1137 insertions(+), 52 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/template/processor/Precondition.java create mode 100644 src/test/java/org/openrewrite/java/template/processor/PreconditionTest.java create mode 100644 src/test/resources/refaster/PreconditionsVerifier.java create mode 100644 src/test/resources/refaster/PreconditionsVerifierRecipes.java diff --git a/src/main/java/org/openrewrite/java/template/processor/Precondition.java b/src/main/java/org/openrewrite/java/template/processor/Precondition.java new file mode 100644 index 00000000..365c825e --- /dev/null +++ b/src/main/java/org/openrewrite/java/template/processor/Precondition.java @@ -0,0 +1,193 @@ +/* + * 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.java.template.processor; + +import lombok.EqualsAndHashCode; +import lombok.Value; + +import java.util.*; + +import static java.util.stream.Collectors.toCollection; + +public abstract class Precondition { + private static final Comparator BY_USES_TYPE_FIRST = Comparator + .comparing((String s) -> !s.startsWith("new UsesType")) + .thenComparing(Comparator.naturalOrder()); + + abstract boolean fitsInto(Precondition p); + + public Precondition prune() { + return this; + } + + @Value + @EqualsAndHashCode(callSuper = false) + public static class Rule extends Precondition { + String rule; + + @Override + boolean fitsInto(Precondition p) { + if (p instanceof Rule) { + return this.equals(p); + } else if (p instanceof Or) { + return ((Or) p).preconditions.stream().anyMatch(this::fitsInto); + } else if (p instanceof And) { + return ((And) p).preconditions.stream().anyMatch(this::fitsInto); + } + return false; // unreachable code + } + + @Override + public String toString() { + return rule; + } + } + + @Value + @EqualsAndHashCode(callSuper = false, of = "preconditions") + public static class Or extends Precondition { + Set preconditions; + int indent; + + @Override + boolean fitsInto(Precondition p) { + if (p instanceof Or) { + return this.equals(p); + } + return false; + } + + @Override + public Precondition prune() { + Precondition pruned = takeElementIfItFitsInAllOtherElements(); + return pruned == null ? extractCommonElements() : pruned; + } + + /** + * If element fits in all others, take element as precondition. Eg: + *

+         * or(
+         *    and(new UsesType<>("Map"), new UsesMethod<>("PrintStream println(..)")),
+         *    new UsesMethod<>("PrintStream println(..)")
+         * )
+         * 
+ *

+ * will return: + *

+         * new UsesMethod<>("PrintStream println()")
+         * 
+ */ + private Precondition takeElementIfItFitsInAllOtherElements() { + for (Precondition p : preconditions) { + int matches = 0; + for (Precondition p2 : preconditions) { + if (p == p2 || p.fitsInto(p2)) { + matches++; + } + if (matches == preconditions.size()) { + return p; + } + } + } + return null; + } + + /** + * If child element of an element exist as child element in all others, move child element up. Eg: + *
+         * or(
+         *    and(new UsesType<>("Map"), new UsesType<>("HashMap"), new UsesMethod<>("PrintStream println(..)")),
+         *    and(new UsesType<>("List"), new UsesType<>("ArrayList"), new UsesMethod<>("PrintStream println(..)"))
+         * )
+         * 
+ *

+ * will return: + *

+         * and(
+         *    new UsesMethod<>("PrintStream println()"),
+         *    or(
+         *      and(new UsesType<>("Map"), new UsesType<>("HashMap")),
+         *      and(new UsesType<>("List"), new UsesType<>("ArrayList")),
+         *    )
+         * )
+         * 
+ */ + private Precondition extractCommonElements() { + boolean first = true; + Set commons = new HashSet<>(); + for (Precondition p : preconditions) { + if (!(p instanceof And)) { + return this; + } + if (first) { + commons.addAll(((And) p).preconditions); + first = false; + } else { + commons.retainAll(((And) p).preconditions); + } + } + + if (!commons.isEmpty()) { + preconditions.forEach(it -> ((And) it).preconditions.removeAll(commons)); + commons.add(new Or(preconditions, indent)); + return new And(commons, indent); + } + + return this; + } + + @Override + public String toString() { + return joinPreconditions(preconditions, "or", indent); + } + } + + @Value + @EqualsAndHashCode(callSuper = false, of = "preconditions") + public static class And extends Precondition { + Set preconditions; + int indent; + + @Override + boolean fitsInto(Precondition p) { + if (p instanceof And) { + if (preconditions.size() > ((And) p).preconditions.size()) { + return false; + } + return preconditions.stream().allMatch(it -> it.fitsInto(p)); + } + return false; + } + + @Override + public String toString() { + return joinPreconditions(preconditions, "and", indent); + } + } + + private static String joinPreconditions(Collection rules, String op, int indent) { + if (rules.isEmpty()) { + return ""; + } else if (rules.size() == 1) { + return rules.iterator().next().toString(); + } + String whitespace = String.format("%" + indent + "s", " "); + Set preconditions = rules.stream().map(Object::toString).sorted(BY_USES_TYPE_FIRST).collect(toCollection(LinkedHashSet::new)); + return "Preconditions." + op + "(\n" + + whitespace + String.join(",\n" + whitespace, preconditions) + "\n" + + whitespace.substring(0, indent - 4) + ')'; + } +} diff --git a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java index 154dfd68..cc6f980e 100644 --- a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java @@ -226,7 +226,7 @@ public void visitClassDef(JCTree.JCClassDecl classDecl) { String javaVisitor = newAbstractRefasterJavaVisitor(beforeTemplates, after, descriptor); - String preconditions = generatePreconditions(descriptor.beforeTemplates, 16); + Precondition preconditions = generatePreconditions(descriptor.beforeTemplates, 16); if (preconditions == null) { recipe.append(String.format(" return %s;\n", javaVisitor)); } else { @@ -587,18 +587,17 @@ private Set getImportsAsStrings(Map> imp } /* Generate the minimal precondition that would allow to match each before template individually. */ - @SuppressWarnings("SameParameterValue") - private @Nullable String generatePreconditions(List beforeTemplates, int indent) { - Map> preconditions = new LinkedHashMap<>(); + private @Nullable Precondition generatePreconditions(List beforeTemplates, int indent) { + Map> preconditions = new LinkedHashMap<>(); for (TemplateDescriptor beforeTemplate : beforeTemplates) { int arity = beforeTemplate.getArity(); for (int i = 0; i < arity; i++) { - Set usesVisitors = new LinkedHashSet<>(); + Set usesVisitors = new LinkedHashSet<>(); for (Symbol.ClassSymbol usedType : beforeTemplate.usedTypes(i)) { String name = usedType.getQualifiedName().toString().replace('$', '.'); if (!name.startsWith("java.lang.") && !name.startsWith("com.google.errorprone.refaster.")) { - usesVisitors.add("new UsesType<>(\"" + name + "\", true)"); + usesVisitors.add(new Precondition.Rule("new UsesType<>(\"" + name + "\", true)")); } } for (Symbol.MethodSymbol method : beforeTemplate.usedMethods(i)) { @@ -607,48 +606,28 @@ private Set getImportsAsStrings(Map> imp } String methodName = method.name.toString(); methodName = methodName.equals("") ? "" : methodName; - usesVisitors.add("new UsesMethod<>(\"" + method.owner.getQualifiedName().toString() + ' ' + methodName + "(..)\", true)"); + usesVisitors.add(new Precondition.Rule(String.format("new UsesMethod<>(\"%s %s(..)\", true)", + method.owner.getQualifiedName().toString(), methodName))); } - preconditions.put(beforeTemplate.method.name.toString() + (arity == 1 ? "" : "$" + i), usesVisitors); - } - } - - if (preconditions.size() == 1) { - return joinPreconditions(preconditions.values().iterator().next(), "and", indent + 4); - } else if (preconditions.size() > 1) { - Set common = new LinkedHashSet<>(); - for (String dep : preconditions.values().iterator().next()) { - if (preconditions.values().stream().allMatch(v -> v.contains(dep))) { - common.add(dep); - } - } - common.forEach(dep -> preconditions.values().forEach(v -> v.remove(dep))); - preconditions.values().removeIf(Collection::isEmpty); - - if (common.isEmpty()) { - return joinPreconditions(preconditions.values().stream().map(v -> joinPreconditions(v, "and", indent + 4)).collect(toList()), "or", indent + 4); - } else { - if (!preconditions.isEmpty()) { - String uniqueConditions = joinPreconditions(preconditions.values().stream().map(v -> joinPreconditions(v, "and", indent + 12)).collect(toList()), "or", indent + 8); - common.add(uniqueConditions); + if (!usesVisitors.isEmpty()) { + preconditions.put(beforeTemplate.method.name.toString() + (arity == 1 ? "" : "$" + i), usesVisitors); + } else { + return null; // At least one of the before templates has no preconditions, so we can not use any preconditions } - return joinPreconditions(common, "and", indent + 4); } } - return null; - } - private String joinPreconditions(Collection preconditions, String op, int indent) { if (preconditions.isEmpty()) { return null; - } else if (preconditions.size() == 1) { - return preconditions.iterator().next(); } - char[] indentChars = new char[indent]; - Arrays.fill(indentChars, ' '); - String indentStr = new String(indentChars); - return "Preconditions." + op + "(\n" + indentStr + String.join(",\n" + indentStr, preconditions) + "\n" + indentStr.substring(0, indent - 4) + ')'; + + return new Precondition.Or( + preconditions.values().stream() + .map(it -> new Precondition.And(it, indent + 4)) + .collect(toSet()) + , indent + 4) + .prune(); } }.scan(cu); } @@ -751,7 +730,7 @@ public RuleDescriptor(JCTree.JCClassDecl classDecl, JCCompilationUnit cu, Contex this.context = context; } - private RefasterTemplateProcessor.@Nullable RuleDescriptor validate() { + private @Nullable RuleDescriptor validate() { if (beforeTemplates.isEmpty()) { return null; } @@ -1089,7 +1068,7 @@ private static List getTemplateAnnotations(MethodTree metho result.add((JCTree.JCAnnotation) annotation); } else if (type.getKind() == Tree.Kind.MEMBER_SELECT && type instanceof JCTree.JCFieldAccess && ((JCTree.JCFieldAccess) type).sym != null && - typePredicate.test(((JCTree.JCFieldAccess) type).sym.getQualifiedName().toString())) { + typePredicate.test(((JCTree.JCFieldAccess) type).sym.getQualifiedName().toString())) { result.add((JCTree.JCAnnotation) annotation); } } diff --git a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java index c659e2d3..95f3a2c2 100644 --- a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java @@ -96,6 +96,7 @@ void skipRecipeGeneration(String recipeName) { "SimplifyTernary", "RefasterAnyOf", "Parameters", + "PreconditionsVerifier", }) void nestedRecipes(String recipeName) { Compilation compilation = compileResource("refaster/" + recipeName + ".java"); diff --git a/src/test/java/org/openrewrite/java/template/processor/PreconditionTest.java b/src/test/java/org/openrewrite/java/template/processor/PreconditionTest.java new file mode 100644 index 00000000..127ef7fe --- /dev/null +++ b/src/test/java/org/openrewrite/java/template/processor/PreconditionTest.java @@ -0,0 +1,205 @@ +/* + * 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.java.template.processor; + +import org.junit.jupiter.api.Test; +import org.openrewrite.java.template.processor.Precondition.And; +import org.openrewrite.java.template.processor.Precondition.Or; +import org.openrewrite.java.template.processor.Precondition.Rule; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.truth.Truth.assertThat; + +class PreconditionTest { + @Test + void toStringWithInden() { + String result = new Or( + setOf( + new And( + setOf( + new Rule("A"), + new Rule("B"), + new Rule("C")), + 4 + ), + new And( + setOf( + new Rule("X"), + new Rule("Y"), + new Rule("Z")), + 4 + ) + ), 4).toString(); + + assertThat(result).isEqualTo("Preconditions.or(\n" + + " Preconditions.and(\n" + + " A,\n" + + " B,\n" + + " C\n" + + "),\n" + + " Preconditions.and(\n" + + " X,\n" + + " Y,\n" + + " Z\n" + + ")\n" + + ")"); + } + + @Test + void ruleFitsInRule() { + assertThat(new Rule("A").fitsInto(new Rule("A"))).isTrue(); + } + + @Test + void orFitsInOr() { + boolean result = new Or( + setOf(new Rule("A"), new Rule("B")), + 4 + ).fitsInto(new Or( + setOf(new Rule("B"), new Rule("A")), + 4 + )); + + assertThat(result).isTrue(); + } + + @Test + void ardFitsNotInAndWithDifferentRules() { + boolean result = new Or( + setOf(new Rule("A"), new Rule("C")), + 4 + ).fitsInto(new Or( + setOf(new Rule("B"), new Rule("A")), + 4 + )); + + assertThat(result).isFalse(); + } + + @Test + void andFitsInAnd() { + boolean result = new And( + setOf(new Rule("A")), + 4 + ).fitsInto(new And( + setOf(new Rule("B"), new Rule("A")), + 4 + )); + + assertThat(result).isTrue(); + } + + @Test + void andFitsNotInAndWithDifferentRules() { + boolean result = new And( + setOf(new Rule("A"), new Rule("C")), + 4 + ).fitsInto(new And( + setOf(new Rule("B"), new Rule("A")), + 4 + )); + + assertThat(result).isFalse(); + } + + @Test + void sameRulesArePrunedAutomatically() { + Set result = setOf(new Rule("A"), new Rule("A")); + + assertThat(result).isEqualTo(setOf(new Rule("A"))); + } + + @Test + void sameRulesArePrunedAutomaticallyInAnOr() { + Precondition result = new Or( + setOf(new Rule("A"), new Rule("A")), + 4 + ); + + assertThat(result).isEqualTo(new Or( + setOf(new Rule("A")), + 4 + )); + } + + @Test + void pruneOrWithAnds() { + Precondition result = new Or( + setOf( + new And( + setOf(new Rule("A"), new Rule("B")), + 4 + ), + new And( + setOf(new Rule("A"), new Rule("B"), new Rule("C")), + 4 + ) + ), 4).prune(); + + assertThat(result).isEqualTo(new And( + setOf(new Rule("A"), new Rule("B")), + 4 + )); + } + + @Test + void pruneOrWithAndAndRule() { + Precondition result = new Or( + setOf( + new And( + setOf(new Rule("A"), new Rule("B")), + 4 + ), + new Rule("B") + ), 4).prune(); + + assertThat(result).isEqualTo(new Rule("B")); + } + + @Test + void pruneOrWithTypeChange() { + Precondition result = new Or( + setOf( + new And( + setOf(new Rule("A"), new Rule("B"), new Rule("C")), + 4 + ), + new And( + setOf(new Rule("D"), new Rule("B"), new Rule("E")), + 4 + ) + ), 4).prune(); + + assertThat(result).isEqualTo(new And( + setOf( + new Rule("B"), + new Or( + setOf( + new And(setOf(new Rule("A"), new Rule("C")), 4), + new And(setOf(new Rule("D"), new Rule("E")), 4) + ), 4 + ) + ), 4)); + } + + @SafeVarargs + private static Set setOf(T... rules) { + return new HashSet<>(Arrays.asList(rules)); + } +} diff --git a/src/test/resources/refaster/EscapesRecipes.java b/src/test/resources/refaster/EscapesRecipes.java index 3d56a747..f8d7e3e2 100644 --- a/src/test/resources/refaster/EscapesRecipes.java +++ b/src/test/resources/refaster/EscapesRecipes.java @@ -117,8 +117,8 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { return Preconditions.check( Preconditions.and( new UsesType<>("com.sun.tools.javac.util.Convert", true), - new UsesMethod<>("java.lang.String format(..)", true), - new UsesMethod<>("com.sun.tools.javac.util.Convert quote(..)", true) + new UsesMethod<>("com.sun.tools.javac.util.Convert quote(..)", true), + new UsesMethod<>("java.lang.String format(..)", true) ), javaVisitor ); diff --git a/src/test/resources/refaster/MethodThrowsRecipe.java b/src/test/resources/refaster/MethodThrowsRecipe.java index 4820dee8..34529869 100644 --- a/src/test/resources/refaster/MethodThrowsRecipe.java +++ b/src/test/resources/refaster/MethodThrowsRecipe.java @@ -85,8 +85,8 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( Preconditions.and( - new UsesType<>("java.nio.file.Files", true), new UsesType<>("java.nio.charset.StandardCharsets", true), + new UsesType<>("java.nio.file.Files", true), new UsesType<>("java.nio.file.Path", true), new UsesMethod<>("java.nio.file.Files readAllLines(..)", true) ), diff --git a/src/test/resources/refaster/PreconditionsVerifier.java b/src/test/resources/refaster/PreconditionsVerifier.java new file mode 100644 index 00000000..3197b5af --- /dev/null +++ b/src/test/resources/refaster/PreconditionsVerifier.java @@ -0,0 +1,147 @@ +/* + * Copyright 2023 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 foo; + +import com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.BeforeTemplate; +import com.sun.tools.javac.util.Convert; + +import java.util.List; +import java.util.Map; + +/** + * A refaster template to test when a `UsesType`and Preconditions.or should or should not be applied to the Preconditions check. + */ +public class PreconditionsVerifier { + public static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrString { + @BeforeTemplate + void doubleAndInt(double actual, int ignore) { + System.out.println(actual); + } + + @BeforeTemplate + void stringAndString(String actual, String ignore) { + System.out.println(actual); + } + + @AfterTemplate + void after(Object actual) { + System.out.println("Changed: " + actual); + } + } + + public static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBody { + @BeforeTemplate + String string(String value) { + return Convert.quote(value); + } + + @BeforeTemplate + String _int(int value) { + return String.valueOf(value); + } + + @AfterTemplate + Object after(Object actual) { + return Convert.quote(String.valueOf(actual)); + } + } + + public static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBody { + @BeforeTemplate + String string(String value) { + return Convert.quote(value); + } + + @BeforeTemplate + String _int(int value) { + return Convert.quote(String.valueOf(value)); + } + + @AfterTemplate + Object after(Object actual) { + return Convert.quote(String.valueOf(actual)); + } + } + + public static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherType { + @BeforeTemplate + void _int(int actual) { + System.out.println(actual); + } + + @BeforeTemplate + void map(Map actual) { + System.out.println(actual); + } + + @AfterTemplate + void after(Object actual) { + System.out.println("Changed: " + actual); + } + } + + public static class NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherType { + @BeforeTemplate + void string(String actual) { + System.out.println(actual); + } + + @BeforeTemplate + void map(Map actual) { + System.out.println(actual); + } + + @AfterTemplate + void after(Object actual) { + System.out.println("Changed: " + actual); + } + } + + public static class UsesTypeMapWhenAllBeforeTemplatesContainsMap { + @BeforeTemplate + void mapWithGeneric(Map actual) { + System.out.println(actual); + } + + @BeforeTemplate + void mapWithGenericTwo(Map actual) { + System.out.println(actual); + } + + @AfterTemplate + void mapWithoutGeneric(Map actual) { + System.out.println("Changed: " + actual); + } + } + + public static class UsesTypeMapOrListWhenBeforeTemplateContainsMapAndList { + @BeforeTemplate + void list(List actual) { + System.out.println(actual); + } + + @BeforeTemplate + void map(Map actual) { + System.out.println(actual); + } + + @AfterTemplate + void after(Object actual) { + System.out.println("Changed: " + actual); + } + } +} diff --git a/src/test/resources/refaster/PreconditionsVerifierRecipes.java b/src/test/resources/refaster/PreconditionsVerifierRecipes.java new file mode 100644 index 00000000..eae10ce8 --- /dev/null +++ b/src/test/resources/refaster/PreconditionsVerifierRecipes.java @@ -0,0 +1,560 @@ +/* + * 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 foo; + +import org.jspecify.annotations.NullMarked; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.search.*; +import org.openrewrite.java.template.Primitive; +import org.openrewrite.java.template.function.*; +import org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor; +import org.openrewrite.java.tree.*; + +import javax.annotation.Generated; +import java.util.*; + +import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.*; + +/** + * OpenRewrite recipes created for Refaster template {@code foo.PreconditionsVerifier}. + */ +@SuppressWarnings("all") +@Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") +public class PreconditionsVerifierRecipes extends Recipe { + /** + * Instantiates a new instance. + */ + public PreconditionsVerifierRecipes() {} + + @Override + public String getDisplayName() { + return "A refaster template to test when a `UsesType`and Preconditions.or should or should not be applied to the Preconditions check"; + } + + @Override + public String getDescription() { + return "Refaster template recipes for `foo.PreconditionsVerifier`."; + } + + @Override + public List getRecipeList() { + return Arrays.asList( + new NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrStringRecipe(), + new UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBodyRecipe(), + new UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBodyRecipe(), + new NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherTypeRecipe(), + new NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherTypeRecipe(), + new UsesTypeMapWhenAllBeforeTemplatesContainsMapRecipe(), + new UsesTypeMapOrListWhenBeforeTemplateContainsMapAndListRecipe() + ); + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrString}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrStringRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrStringRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrString`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveOrString {\n \n @BeforeTemplate()\n void doubleAndInt(double actual, int ignore) {\n System.out.println(actual);\n }\n \n @BeforeTemplate()\n void stringAndString(String actual, String ignore) {\n System.out.println(actual);\n }\n \n @AfterTemplate()\n void after(Object actual) {\n System.out.println(\"Changed: \" + actual);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate doubleAndInt = JavaTemplate + .builder("System.out.println(#{actual:any(double)});") + .build(); + final JavaTemplate stringAndString = JavaTemplate + .builder("System.out.println(#{actual:any(java.lang.String)});") + .build(); + final JavaTemplate after = JavaTemplate + .builder("System.out.println(\"Changed: \" + #{actual:any(java.lang.Object)});") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = doubleAndInt.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = stringAndString.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + new UsesMethod<>("java.io.PrintStream println(..)", true), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBody}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBodyRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBodyRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBody`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInSomeBeforeBody {\n \n @BeforeTemplate()\n String string(String value) {\n return Convert.quote(value);\n }\n \n @BeforeTemplate()\n String _int(int value) {\n return String.valueOf(value);\n }\n \n @AfterTemplate()\n Object after(Object actual) {\n return Convert.quote(String.valueOf(actual));\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate string = JavaTemplate + .builder("com.sun.tools.javac.util.Convert.quote(#{value:any(java.lang.String)})") + .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())) + .build(); + final JavaTemplate _int = JavaTemplate + .builder("String.valueOf(#{value:any(int)})") + .build(); + final JavaTemplate after = JavaTemplate + .builder("com.sun.tools.javac.util.Convert.quote(String.valueOf(#{actual:any(java.lang.Object)}))") + .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())) + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = string.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = _int.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + Preconditions.or( + Preconditions.and( + new UsesType<>("com.sun.tools.javac.util.Convert", true), + new UsesMethod<>("com.sun.tools.javac.util.Convert quote(..)", true) + ), + new UsesMethod<>("java.lang.String valueOf(..)", true) + ), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBody}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBodyRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBodyRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBody`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class UsesTypeWhenBeforeTemplateContainsPrimitiveOrStringAndTypeInAllBeforeBody {\n \n @BeforeTemplate()\n String string(String value) {\n return Convert.quote(value);\n }\n \n @BeforeTemplate()\n String _int(int value) {\n return Convert.quote(String.valueOf(value));\n }\n \n @AfterTemplate()\n Object after(Object actual) {\n return Convert.quote(String.valueOf(actual));\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate string = JavaTemplate + .builder("com.sun.tools.javac.util.Convert.quote(#{value:any(java.lang.String)})") + .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())) + .build(); + final JavaTemplate _int = JavaTemplate + .builder("com.sun.tools.javac.util.Convert.quote(String.valueOf(#{value:any(int)}))") + .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())) + .build(); + final JavaTemplate after = JavaTemplate + .builder("com.sun.tools.javac.util.Convert.quote(String.valueOf(#{actual:any(java.lang.Object)}))") + .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())) + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = string.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = _int.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + Preconditions.and( + new UsesType<>("com.sun.tools.javac.util.Convert", true), + new UsesMethod<>("com.sun.tools.javac.util.Convert quote(..)", true) + ), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherType}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherTypeRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherTypeRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherType`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class NoUsesTypeWhenBeforeTemplateContainsPrimitiveAndAnotherType {\n \n @BeforeTemplate()\n void _int(int actual) {\n System.out.println(actual);\n }\n \n @BeforeTemplate()\n void map(Map actual) {\n System.out.println(actual);\n }\n \n @AfterTemplate()\n void after(Object actual) {\n System.out.println(\"Changed: \" + actual);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate _int = JavaTemplate + .builder("System.out.println(#{actual:any(int)});") + .build(); + final JavaTemplate map = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.Map)});") + .build(); + final JavaTemplate after = JavaTemplate + .builder("System.out.println(\"Changed: \" + #{actual:any(java.lang.Object)});") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = _int.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = map.matcher(getCursor())).find()) { + maybeRemoveImport("java.util.Map"); + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + new UsesMethod<>("java.io.PrintStream println(..)", true), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherType}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherTypeRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherTypeRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherType`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class NoUsesTypeWhenBeforeTemplateContainsStringAndAnotherType {\n \n @BeforeTemplate()\n void string(String actual) {\n System.out.println(actual);\n }\n \n @BeforeTemplate()\n void map(Map actual) {\n System.out.println(actual);\n }\n \n @AfterTemplate()\n void after(Object actual) {\n System.out.println(\"Changed: \" + actual);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate string = JavaTemplate + .builder("System.out.println(#{actual:any(java.lang.String)});") + .build(); + final JavaTemplate map = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.Map)});") + .build(); + final JavaTemplate after = JavaTemplate + .builder("System.out.println(\"Changed: \" + #{actual:any(java.lang.Object)});") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = string.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = map.matcher(getCursor())).find()) { + maybeRemoveImport("java.util.Map"); + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + new UsesMethod<>("java.io.PrintStream println(..)", true), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.UsesTypeMapWhenAllBeforeTemplatesContainsMap}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class UsesTypeMapWhenAllBeforeTemplatesContainsMapRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public UsesTypeMapWhenAllBeforeTemplatesContainsMapRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.UsesTypeMapWhenAllBeforeTemplatesContainsMap`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class UsesTypeMapWhenAllBeforeTemplatesContainsMap {\n \n @BeforeTemplate()\n void mapWithGeneric(Map actual) {\n System.out.println(actual);\n }\n \n @BeforeTemplate()\n void mapWithGenericTwo(Map actual) {\n System.out.println(actual);\n }\n \n @AfterTemplate()\n void mapWithoutGeneric(Map actual) {\n System.out.println(\"Changed: \" + actual);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate mapWithGeneric = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.Map)});") + .build(); + final JavaTemplate mapWithGenericTwo = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.Map)});") + .build(); + final JavaTemplate mapWithoutGeneric = JavaTemplate + .builder("System.out.println(\"Changed: \" + #{actual:any(java.util.Map)});") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = mapWithGeneric.matcher(getCursor())).find()) { + return embed( + mapWithoutGeneric.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = mapWithGenericTwo.matcher(getCursor())).find()) { + return embed( + mapWithoutGeneric.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + Preconditions.and( + new UsesType<>("java.util.Map", true), + new UsesMethod<>("java.io.PrintStream println(..)", true) + ), + javaVisitor + ); + } + } + + /** + * OpenRewrite recipe created for Refaster template {@code PreconditionsVerifier.UsesTypeMapOrListWhenBeforeTemplateContainsMapAndList}. + */ + @SuppressWarnings("all") + @NullMarked + @Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") + public static class UsesTypeMapOrListWhenBeforeTemplateContainsMapAndListRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public UsesTypeMapOrListWhenBeforeTemplateContainsMapAndListRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PreconditionsVerifier.UsesTypeMapOrListWhenBeforeTemplateContainsMapAndList`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class UsesTypeMapOrListWhenBeforeTemplateContainsMapAndList {\n \n @BeforeTemplate()\n void list(List actual) {\n System.out.println(actual);\n }\n \n @BeforeTemplate()\n void map(Map actual) {\n System.out.println(actual);\n }\n \n @AfterTemplate()\n void after(Object actual) {\n System.out.println(\"Changed: \" + actual);\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate list = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.List)});") + .build(); + final JavaTemplate map = JavaTemplate + .builder("System.out.println(#{actual:any(java.util.Map)});") + .build(); + final JavaTemplate after = JavaTemplate + .builder("System.out.println(\"Changed: \" + #{actual:any(java.lang.Object)});") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = list.matcher(getCursor())).find()) { + maybeRemoveImport("java.util.List"); + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + if ((matcher = map.matcher(getCursor())).find()) { + maybeRemoveImport("java.util.Map"); + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + Preconditions.and( + Preconditions.or( + new UsesType<>("java.util.List", true), + new UsesType<>("java.util.Map", true) + ), + new UsesMethod<>("java.io.PrintStream println(..)", true) + ), + javaVisitor + ); + } + } + +} diff --git a/src/test/resources/refaster/RefasterAnyOfRecipes.java b/src/test/resources/refaster/RefasterAnyOfRecipes.java index 9d32c614..ef2cd7c1 100644 --- a/src/test/resources/refaster/RefasterAnyOfRecipes.java +++ b/src/test/resources/refaster/RefasterAnyOfRecipes.java @@ -195,13 +195,13 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { Preconditions.and( new UsesType<>("java.util.List", true), Preconditions.or( - Preconditions.and( - new UsesType<>("java.util.LinkedList", true), - new UsesMethod<>("java.util.LinkedList (..)", true) - ), Preconditions.and( new UsesType<>("java.util.Collections", true), new UsesMethod<>("java.util.Collections emptyList(..)", true) + ), + Preconditions.and( + new UsesType<>("java.util.LinkedList", true), + new UsesMethod<>("java.util.LinkedList (..)", true) ) ) ), @@ -271,8 +271,8 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( Preconditions.or( - new UsesMethod<>("java.lang.String valueOf(..)", true), - new UsesMethod<>("java.lang.String copyValueOf(..)", true) + new UsesMethod<>("java.lang.String copyValueOf(..)", true), + new UsesMethod<>("java.lang.String valueOf(..)", true) ), javaVisitor ); diff --git a/src/test/resources/refaster/TwoVisitMethodsRecipe.java b/src/test/resources/refaster/TwoVisitMethodsRecipe.java index e5032f9d..d578ab3e 100644 --- a/src/test/resources/refaster/TwoVisitMethodsRecipe.java +++ b/src/test/resources/refaster/TwoVisitMethodsRecipe.java @@ -101,8 +101,8 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { }; return Preconditions.check( Preconditions.or( - new UsesMethod<>("java.lang.String length(..)", true), - new UsesMethod<>("java.lang.String equals(..)", true) + new UsesMethod<>("java.lang.String equals(..)", true), + new UsesMethod<>("java.lang.String length(..)", true) ), javaVisitor );