Skip to content
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 @@ -30,6 +30,9 @@
import static java.util.Collections.singletonList;

public class EqualsToContentEquals extends Recipe {
private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("String equals(Object)");
private static final MethodMatcher TOSTRING_MATCHER = new MethodMatcher("java.lang.* toString()");

private static final TreeVisitor<?, ExecutionContext> PRECONDITION = Preconditions.or(
new UsesType<>("java.lang.CharSequence", false),
new UsesType<>("java.lang.StringBuffer", false),
Expand All @@ -44,31 +47,26 @@ public class EqualsToContentEquals extends Recipe {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(PRECONDITION, new EqualsToContentEqualsVisitor());
}

private static class EqualsToContentEqualsVisitor extends JavaIsoVisitor<ExecutionContext> {
private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("String equals(Object)");
private static final MethodMatcher TOSTRING_MATCHER = new MethodMatcher("java.lang.* toString()");

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(mi, ctx);
if (!EQUALS_MATCHER.matches(m)) {
return m;
}
Expression equalsArgument = m.getArguments().get(0);
if (!TOSTRING_MATCHER.matches(equalsArgument)) {
return m;
}
J.MethodInvocation inv = (J.MethodInvocation) equalsArgument;
Expression toStringSelect = inv.getSelect();
if (toStringSelect == null || !TypeUtils.isAssignableTo("java.lang.CharSequence", toStringSelect.getType())) {
return m;
return Preconditions.check(PRECONDITION, new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(mi, ctx);
if (!EQUALS_MATCHER.matches(m)) {
return m;
}
Expression equalsArgument = m.getArguments().get(0);
if (!TOSTRING_MATCHER.matches(equalsArgument)) {
return m;
}
J.MethodInvocation inv = (J.MethodInvocation) equalsArgument;
Expression toStringSelect = inv.getSelect();
if (toStringSelect == null || !TypeUtils.isAssignableTo("java.lang.CharSequence", toStringSelect.getType())) {
return m;
}
// Strip out the toString() on the argument and replace with contentEquals
return m.withArguments(singletonList(toStringSelect))
.withName(m.getName().withSimpleName("contentEquals"));
}
// Strip out the toString() on the argument and replace with contentEquals
return m.withArguments(singletonList(toStringSelect))
.withName(m.getName().withSimpleName("contentEquals"));
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import static java.util.Collections.singleton;

public class NoPrimitiveWrappersForToStringOrCompareTo extends Recipe {
private static final MethodMatcher VALUE_OF_NUMBER_MATCHER = new MethodMatcher("java.lang.Number valueOf(*)", true);
private static final MethodMatcher VALUE_OF_BOOLEAN_MATCHER = new MethodMatcher("java.lang.Boolean valueOf(*)", true);

private static final MethodMatcher NUMBER_TO_STRING_MATCHER = new MethodMatcher("java.lang.Number toString()", true);
private static final MethodMatcher BOOLEAN_TO_STRING_MATCHER = new MethodMatcher("java.lang.Boolean toString()", true);

Expand Down Expand Up @@ -63,63 +66,57 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
new UsesMethod<>(BOOLEAN_COMPARE_TO_MATCHER),
new UsesMethod<>(BOOLEAN_TO_STRING_MATCHER)
),
new NoPrimitiveWrapperVisitor()
);
}

private static class NoPrimitiveWrapperVisitor extends JavaIsoVisitor<ExecutionContext> {

private static final MethodMatcher VALUE_OF_NUMBER_MATCHER = new MethodMatcher("java.lang.Number valueOf(*)", true);
private static final MethodMatcher VALUE_OF_BOOLEAN_MATCHER = new MethodMatcher("java.lang.Boolean valueOf(*)", true);

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
JavaType.Class clazz = mi.getMethodType() != null ? TypeUtils.asClass(mi.getMethodType().getDeclaringType()) : null;
if (clazz != null && "java.lang".equals(clazz.getPackageName())) {
if (NUMBER_TO_STRING_MATCHER.matches(mi) || BOOLEAN_TO_STRING_MATCHER.matches(mi)) {
Expression arg = null;
if (mi.getSelect() instanceof J.NewClass) {
arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments());
} else if (mi.getSelect() instanceof J.MethodInvocation) {
J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect();
if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) {
arg = getSingleArg(selectMethod.getArguments());
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
JavaType.Class clazz = mi.getMethodType() != null ? TypeUtils.asClass(mi.getMethodType().getDeclaringType()) : null;
if (clazz != null && "java.lang".equals(clazz.getPackageName())) {
if (NUMBER_TO_STRING_MATCHER.matches(mi) || BOOLEAN_TO_STRING_MATCHER.matches(mi)) {
Expression arg = null;
if (mi.getSelect() instanceof J.NewClass) {
arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments());
} else if (mi.getSelect() instanceof J.MethodInvocation) {
J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect();
if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) {
arg = getSingleArg(selectMethod.getArguments());
}
}
}
if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) {
JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType();
mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null));
//noinspection ArraysAsListWithZeroOrOneArgument
mi = mi.withArguments(Arrays.asList(arg));
}
} else if (NUMBER_COMPARE_TO_MATCHER.matches(mi) || BOOLEAN_COMPARE_TO_MATCHER.matches(mi)) {
Expression arg = null;
if (mi.getSelect() instanceof J.NewClass) {
arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments());
} else if (mi.getSelect() instanceof J.MethodInvocation) {
J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect();
if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) {
arg = getSingleArg(selectMethod.getArguments());
if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) {
JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType();
mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null));
//noinspection ArraysAsListWithZeroOrOneArgument
mi = mi.withArguments(Arrays.asList(arg));
}
} else if (NUMBER_COMPARE_TO_MATCHER.matches(mi) || BOOLEAN_COMPARE_TO_MATCHER.matches(mi)) {
Expression arg = null;
if (mi.getSelect() instanceof J.NewClass) {
arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments());
} else if (mi.getSelect() instanceof J.MethodInvocation) {
J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect();
if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) {
arg = getSingleArg(selectMethod.getArguments());
}
}
}

if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) {
JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType();
mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null));
mi = mi.withArguments(ListUtils.concat(arg, mi.getArguments()));
mi = maybeAutoFormat(mi, mi.withName(mi.getName().withSimpleName("compare")), ctx);
if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) {
JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType();
mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null));
mi = mi.withArguments(ListUtils.concat(arg, mi.getArguments()));
mi = maybeAutoFormat(mi, mi.withName(mi.getName().withSimpleName("compare")), ctx);
}
}
}
return mi;
}
return mi;
}

private @Nullable Expression getSingleArg(@Nullable List<Expression> args) {
if (args != null && args.size() == 1 && !(args.get(0) instanceof J.Empty)) {
return args.get(0);
private @Nullable Expression getSingleArg(@Nullable List<Expression> args) {
if (args != null && args.size() == 1 && !(args.get(0) instanceof J.Empty)) {
return args.get(0);
}
return null;
}
return null;
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,24 @@ public class RemoveHashCodeCallsFromArrayInstances extends Recipe {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesMethod<>(HASHCODE_MATCHER), new RemoveHashCodeCallsFromArrayInstancesVisitor());
}

private static class RemoveHashCodeCallsFromArrayInstancesVisitor extends JavaIsoVisitor<ExecutionContext> {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(methodInvocation, ctx);
return Preconditions.check(new UsesMethod<>(HASHCODE_MATCHER), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(methodInvocation, ctx);

if (HASHCODE_MATCHER.matches(mi)) {
Expression select = mi.getSelect();
if (select != null && select.getType() instanceof JavaType.Array) {
maybeAddImport("java.util.Arrays");
return JavaTemplate.builder("Arrays.hashCode(#{anyArray(java.lang.Object)})")
.imports("java.util.Arrays")
.build()
.apply(getCursor(), mi.getCoordinates().replace(), select);
if (HASHCODE_MATCHER.matches(mi)) {
Expression select = mi.getSelect();
if (select != null && select.getType() instanceof JavaType.Array) {
maybeAddImport("java.util.Arrays");
return JavaTemplate.builder("Arrays.hashCode(#{anyArray(java.lang.Object)})")
.imports("java.util.Arrays")
.build()
.apply(getCursor(), mi.getCoordinates().replace(), select);
}
}
}

return mi;
}
return mi;
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,58 +50,56 @@ public class ReplaceTextBlockWithString extends Recipe {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesJavaVersion<>(13), new ReplaceTextBlockWithStringVisitor());
}

private static class ReplaceTextBlockWithStringVisitor extends JavaVisitor<ExecutionContext> {
return Preconditions.check(new UsesJavaVersion<>(13), new JavaVisitor<ExecutionContext>() {

@Override
public @Nullable J visitLiteral(J.Literal literal, ExecutionContext ctx) {
if (literal.getType() == Primitive.String &&
literal.getValue() != null &&
literal.getValueSource() != null &&
literal.getValueSource().startsWith("\"\"\"")) {
// Split the literal into lines, including trailing empty lines
String[] lines = ((String) literal.getValue()).split("\n", -1);
// Add trailing "\n" to each line but the last one
// If there is only one line and it's empty, then add "\n" to it as well
boolean lastLineIsEmpty = lines[lines.length - 1].isEmpty();
int n = lastLineIsEmpty && lines.length == 1 ? 1 : lines.length - 1;
for (int i = 0; i < n; i++) {
lines[i] += "\\n";
}
// Take all lines except the last one if it's empty
// If there is only one line and it's empty, take it as well
int linesNumber = !lastLineIsEmpty || lines.length == 1 ? lines.length : lines.length - 1;
Expression[] literals = new Expression[linesNumber];
// Add a prefix (possibly containing a comment) of the original literal
literals[0] = toLiteral(lines[0]).withPrefix(literal.getPrefix());
// Add newlines before rest string literals
for (int i = 1; i < linesNumber; i++) {
literals[i] = toLiteral(lines[i]).withPrefix(Space.build("\n", emptyList()));
@Override
public @Nullable J visitLiteral(J.Literal literal, ExecutionContext ctx) {
if (literal.getType() == Primitive.String &&
literal.getValue() != null &&
literal.getValueSource() != null &&
literal.getValueSource().startsWith("\"\"\"")) {
// Split the literal into lines, including trailing empty lines
String[] lines = ((String) literal.getValue()).split("\n", -1);
// Add trailing "\n" to each line but the last one
// If there is only one line and it's empty, then add "\n" to it as well
boolean lastLineIsEmpty = lines[lines.length - 1].isEmpty();
int n = lastLineIsEmpty && lines.length == 1 ? 1 : lines.length - 1;
for (int i = 0; i < n; i++) {
lines[i] += "\\n";
}
// Take all lines except the last one if it's empty
// If there is only one line and it's empty, take it as well
int linesNumber = !lastLineIsEmpty || lines.length == 1 ? lines.length : lines.length - 1;
Expression[] literals = new Expression[linesNumber];
// Add a prefix (possibly containing a comment) of the original literal
literals[0] = toLiteral(lines[0]).withPrefix(literal.getPrefix());
// Add newlines before rest string literals
for (int i = 1; i < linesNumber; i++) {
literals[i] = toLiteral(lines[i]).withPrefix(Space.build("\n", emptyList()));
}
// Format the resulting expression
Expression j = ChainStringBuilderAppendCalls.additiveExpression(literals);
//noinspection DataFlowIssue
return j == null ? null : autoFormat(j, ctx);
}
// Format the resulting expression
Expression j = ChainStringBuilderAppendCalls.additiveExpression(literals);
//noinspection DataFlowIssue
return j == null ? null : autoFormat(j, ctx);
return literal;
}
return literal;
}

private J.Literal toLiteral(String str) {
return new J.Literal(
Tree.randomId(),
Space.EMPTY,
Markers.EMPTY,
str,
quote(str),
emptyList(),
Primitive.String);
}
private J.Literal toLiteral(String str) {
return new J.Literal(
Tree.randomId(),
Space.EMPTY,
Markers.EMPTY,
str,
quote(str),
emptyList(),
Primitive.String);
}

private String quote(String str) {
return "\"" + str.replace("\"", "\\\"") + "\"";
}
private String quote(String str) {
return "\"" + str.replace("\"", "\\\"") + "\"";
}
});
}

}
Loading
Loading