All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.staticanalysis.CombineSemanticallyEqualCatchBlocks Maven / Gradle / Ivy

/*
 * Copyright 2022 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.staticanalysis; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.search.SemanticallyEqual; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.time.Duration; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import static java.util.Collections.emptyMap; @Incubating(since = "7.25.0") public class CombineSemanticallyEqualCatchBlocks extends Recipe { @Override public String getDisplayName() { return "Combine semantically equal catch blocks"; } @Override public String getDescription() { return "Combine catches in a try that contain semantically equivalent blocks. " + "No change will be made when a caught exception exists if combing catches may change application behavior or type attribution is missing."; } @Override public Set getTags() { return Collections.singleton("RSPEC-S2147"); } @Override public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(5); } @Override public TreeVisitor getVisitor() { return new CombineSemanticallyEqualCatchBlocksVisitor(); } private static class CombineSemanticallyEqualCatchBlocksVisitor extends JavaVisitor { @Override public J visitTry(J.Try tryable, ExecutionContext ctx) { J.Try t = (J.Try) super.visitTry(tryable, ctx); Map> semanticallyEqualCatchesMap = new LinkedHashMap<>(); List catches = t.getCatches(); // Check if the try contains semantically equal catch blocks. for (int i = 0; i < catches.size(); i++) { J.Try.Catch from = catches.get(i); for (int j = i + 1; j < catches.size(); j++) { J.Try.Catch to = catches.get(j); if (SemanticallyEqual.areEqual(from.getBody(), to.getBody()) && containSameComments(from.getBody(), to.getBody())) { List semanticallyEqualCatch = semanticallyEqualCatchesMap.computeIfAbsent(from, k -> new ArrayList<>()); semanticallyEqualCatch.add(to); } } } if (!semanticallyEqualCatchesMap.isEmpty()) { // Collect the identifiers of caught exceptions that are subtypes or implementations of an exception that is caught later in a different catch. Map>> parentChildClassRelationship = new HashMap<>(); for (int i = 0; i < catches.size(); i++) { J.Try.Catch from = catches.get(i); for (int j = i + 1; j < catches.size(); j++) { J.Try.Catch to = catches.get(j); // Both 'from' and 'to' may be multi-catches. for (J.Identifier fromIdentifier : getCaughtExceptions(from)) { for (J.Identifier toIdentifier : getCaughtExceptions(to)) { if (fromIdentifier.getType() != null && toIdentifier.getType() != null && TypeUtils.isAssignableTo(toIdentifier.getType(), fromIdentifier.getType())) { Map> subTypesMap = parentChildClassRelationship.computeIfAbsent(from, key -> new HashMap<>()); Set childClassIdentifiers = subTypesMap.computeIfAbsent(to, key -> new HashSet<>()); childClassIdentifiers.add(fromIdentifier); } } } } } // Collect the catches that are safe to combine. Map> combineCatchesMap = new HashMap<>(); for (Map.Entry> semanticallyEqualCatches : semanticallyEqualCatchesMap.entrySet()) { J.Try.Catch from = semanticallyEqualCatches.getKey(); OUTER: for (J.Try.Catch to : semanticallyEqualCatches.getValue()) { // Check if any catch exists between two catches with semantically equal blocks that is not semantically equal. int indexFrom = catches.indexOf(from); int indexTo = catches.indexOf(to); if ((indexTo - indexFrom) != 1) { // Sequential catches are always safe to combine. I.E. Catch 1 followed by Catch 2. int start = indexFrom + 1; int end = indexTo - 1; for (; start <= end; start++) { J.Try.Catch mayChangeApplicationBehavior = catches.get(start); if (parentChildClassRelationship.containsKey(mayChangeApplicationBehavior) && parentChildClassRelationship.get(t.getCatches().get(start)).containsKey(to)) { if (!semanticallyEqualCatchesMap.containsKey(mayChangeApplicationBehavior)) { // Skip because combining the catches may change application behavior. continue OUTER; } } } } List toCatches = combineCatchesMap.computeIfAbsent(from, key -> new ArrayList<>()); toCatches.add(to); } } for (Map.Entry> catchMapEntry : combineCatchesMap.entrySet()) { doAfterVisit(new CombineCatches( catchMapEntry.getKey(), catchMapEntry.getValue(), parentChildClassRelationship.getOrDefault(catchMapEntry.getKey(), emptyMap()))); doAfterVisit(new RemoveCatches(catchMapEntry.getValue())); } } return t; } @SuppressWarnings("ConstantConditions") static class RemoveCatches extends JavaVisitor { private final List removeCatches; RemoveCatches(@Nullable List removeCatches) { this.removeCatches = removeCatches; } @Override public @Nullable J visitMultiCatch(J.MultiCatch multiCatch, ExecutionContext ctx) { Cursor parentCursor = getCursor().dropParentUntil(is -> is instanceof J.Try.Catch || is instanceof J.Try); if (removeCatches != null && parentCursor.getValue() instanceof J.Try.Catch) { if (removeCatches.contains((J.Try.Catch) parentCursor.getValue())) { return null; } } return super.visitMultiCatch(multiCatch, ctx); } @Override public @Nullable J visitCatch(J.Try.Catch _catch, ExecutionContext ctx) { if (removeCatches != null) { if (removeCatches.contains(_catch)) { return null; } } return super.visitCatch(_catch, ctx); } } private static class CombineCatches extends JavaVisitor { private final J.Try.Catch scope; private final List equivalentCatches; private final Map> childClassesToExclude; CombineCatches(J.Try.Catch scope, List equivalentCatches, Map> childClassesToExclude) { this.scope = scope; this.equivalentCatches = equivalentCatches; this.childClassesToExclude = childClassesToExclude; } @Override public J visitMultiCatch(J.MultiCatch multiCatch, ExecutionContext ctx) { J.MultiCatch m = (J.MultiCatch) super.visitMultiCatch(multiCatch, ctx); Cursor parentCursor = getCursor().dropParentUntil(is -> is instanceof J.Try.Catch || is instanceof J.Try); if (parentCursor.getValue() instanceof J.Try.Catch) { J.Try.Catch parent = parentCursor.getValue(); if (parent == scope) { List> combinedCatches = combineEquivalentCatches(); m = maybeAutoFormat(m, m.getPadding().withAlternatives(combinedCatches), ctx); } } return m; } @Override public J visitCatch(J.Try.Catch _catch, ExecutionContext ctx) { J.Try.Catch c = (J.Try.Catch) super.visitCatch(_catch, ctx); if (c == scope && !isMultiCatch(c)) { if (c.getParameter().getTree().getTypeExpression() != null) { List> combinedCatches = combineEquivalentCatches(); c = maybeAutoFormat(c, c.withParameter(c.getParameter() .withTree(c.getParameter().getTree() .withTypeExpression(new J.MultiCatch(Tree.randomId(), Space.EMPTY, Markers.EMPTY, combinedCatches)))), ctx); } } return c; } private List> combineEquivalentCatches() { Set removeIdentifiers = new HashSet<>(); List> combinedCatches = new ArrayList<>(); for (J.Try.Catch equivalentCatch : equivalentCatches) { Set childClasses = childClassesToExclude.get(equivalentCatch); if (childClasses != null) { // Remove child classes that will be unnecessary since the parent exists in the new multi-catch. removeIdentifiers.addAll(childClasses); } // Whitespace works slightly differently between single catches and multi-catches. // The prefix of each `J.Identifier` is set to `Space.EMPTY` so that auto-format will make all the appropriate changes. if (isMultiCatch(equivalentCatch)) { if (equivalentCatch.getParameter().getTree().getTypeExpression() != null) { J.MultiCatch newMultiCatch = (J.MultiCatch) equivalentCatch.getParameter().getTree().getTypeExpression(); List> rightPaddedAlternatives = newMultiCatch.getPadding().getAlternatives(); for (JRightPadded alternative : rightPaddedAlternatives) { J.Identifier identifier = (J.Identifier) alternative.getElement(); identifier = identifier.withPrefix(Space.EMPTY); alternative = alternative.withElement(identifier); combinedCatches.add(alternative); } } } else { if (equivalentCatch.getParameter().getTree().getTypeExpression() != null) { J.Identifier identifier = ((J.Identifier) equivalentCatch.getParameter().getTree().getTypeExpression()); identifier = identifier.withPrefix(Space.EMPTY); JRightPadded rightPadded = JRightPadded.build(identifier); combinedCatches.add(rightPadded); } } } // Add exceptions in `scope` last to filter out exceptions that are children of parent classes // that were added into the new catch. if (isMultiCatch(scope)) { J.MultiCatch multiCatch = (J.MultiCatch) scope.getParameter().getTree().getTypeExpression(); if (multiCatch != null) { List> alternatives = multiCatch.getPadding().getAlternatives(); for (int i = alternatives.size() - 1; i >= 0; i--) { if (!removeIdentifiers.contains((J.Identifier) alternatives.get(i).getElement())) { JRightPadded alternative = alternatives.get(i); alternative = alternative.withElement(alternative.getElement().withPrefix(Space.EMPTY)); // Preserve the order of the original catches. combinedCatches.add(0, alternative); } } } } else { J.Identifier identifier = (J.Identifier) scope.getParameter().getTree().getTypeExpression(); if (identifier != null && !removeIdentifiers.contains(identifier)) { identifier = identifier.withPrefix(Space.EMPTY); JRightPadded newCatch = JRightPadded.build(identifier); // Preserve the order of the original catches. combinedCatches.add(0, newCatch); } } return combinedCatches; } } private static boolean containSameComments(J.Block body1, J.Block body2) { CommentVisitor commentVisitor = new CommentVisitor(); commentVisitor.visit(body1, body2); return commentVisitor.isEqual.get(); } /** * This visitor is a slight variation of {@link SemanticallyEqual} that accounts for differences * in comments between two trees. The visitor was separated, because comments are not considered * a part of semantic equivalence. * * Bug fixes related to semantic equality that are found by {@link CombineSemanticallyEqualCatchBlocks} * should be applied to {@link SemanticallyEqual} too. */ @SuppressWarnings("ConstantConditions") private static class CommentVisitor extends JavaIsoVisitor { AtomicBoolean isEqual = new AtomicBoolean(true); private final boolean compareMethodArguments = false; private boolean nullMissMatch(Object obj1, Object obj2) { return (obj1 == null && obj2 != null || obj1 != null && obj2 == null); } private boolean doesNotContainSameComments(Space space1, Space space2) { if (space1 == null && space2 == null) { return false; } if (space1 == null || space2 == null || space1.getComments().size() != space2.getComments().size()) { return true; } for (int i = 0; i < space1.getComments().size(); i++) { Comment comment1 = space1.getComments().get(i); Comment comment2 = space2.getComments().get(i); if (!comment1.printComment(getCursor().getParentOrThrow()).equals(comment2.printComment(getCursor().getParentOrThrow()))) { return true; } } return false; } @Override public Expression visitExpression(Expression expression, J j) { if (isEqual.get()) { if (!TypeUtils.isOfType(expression.getType(), ((Expression) j).getType())) { isEqual.set(false); return expression; } } Expression compareTo = (Expression) j; if (doesNotContainSameComments(expression.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); } return expression; } @Override public J.Annotation visitAnnotation(J.Annotation annotation, J j) { if (isEqual.get()) { if (!(j instanceof J.Annotation)) { isEqual.set(false); return annotation; } J.Annotation compareTo = (J.Annotation) j; if (!TypeUtils.isOfType(annotation.getType(), compareTo.getType()) || nullMissMatch(annotation.getArguments(), compareTo.getArguments()) || annotation.getArguments() != null && compareTo.getArguments() != null && annotation.getArguments().size() != compareTo.getArguments().size() || doesNotContainSameComments(annotation.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return annotation; } this.visitTypeName(annotation.getAnnotationType(), compareTo.getAnnotationType()); if (annotation.getArguments() != null && compareTo.getArguments() != null) { for (int i = 0; i < annotation.getArguments().size(); i++) { this.visit(annotation.getArguments().get(i), compareTo.getArguments().get(i)); } } } return annotation; } @Override public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, J j) { if (isEqual.get()) { if (!(j instanceof J.AnnotatedType)) { isEqual.set(false); return annotatedType; } J.AnnotatedType compareTo = (J.AnnotatedType) j; if (!TypeUtils.isOfType(annotatedType.getType(), compareTo.getType()) || annotatedType.getAnnotations().size() != compareTo.getAnnotations().size() || doesNotContainSameComments(annotatedType.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return annotatedType; } this.visitTypeName(annotatedType.getTypeExpression(), compareTo.getTypeExpression()); for (int i = 0; i < annotatedType.getAnnotations().size(); i++) { this.visit(annotatedType.getAnnotations().get(i), compareTo.getAnnotations().get(i)); } } return annotatedType; } @Override public J.ArrayAccess visitArrayAccess(J.ArrayAccess arrayAccess, J j) { if (isEqual.get()) { if (!(j instanceof J.ArrayAccess)) { isEqual.set(false); return arrayAccess; } J.ArrayAccess compareTo = (J.ArrayAccess) j; if (nullMissMatch(arrayAccess.getType(), compareTo.getType()) || !TypeUtils.isOfType(arrayAccess.getType(), compareTo.getType()) || doesNotContainSameComments(arrayAccess.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return arrayAccess; } this.visit(arrayAccess.getIndexed(), compareTo.getIndexed()); this.visit(arrayAccess.getDimension(), compareTo.getDimension()); } return arrayAccess; } @Override public J.ArrayDimension visitArrayDimension(J.ArrayDimension arrayDimension, J j) { if (isEqual.get()) { if (!(j instanceof J.ArrayDimension)) { isEqual.set(false); return arrayDimension; } J.ArrayDimension compareTo = (J.ArrayDimension) j; if (doesNotContainSameComments(arrayDimension.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return arrayDimension; } this.visit(arrayDimension.getIndex(), compareTo.getIndex()); } return arrayDimension; } @Override public J.ArrayType visitArrayType(J.ArrayType arrayType, J j) { if (isEqual.get()) { if (!(j instanceof J.ArrayType)) { isEqual.set(false); return arrayType; } J.ArrayType compareTo = (J.ArrayType) j; if (!TypeUtils.isOfType(arrayType.getType(), compareTo.getType()) || doesNotContainSameComments(arrayType.getPrefix(), compareTo.getPrefix()) || nullMissMatch(arrayType.getAnnotations(), compareTo.getAnnotations()) || arrayType.getAnnotations().size() != compareTo.getAnnotations().size()) { isEqual.set(false); return arrayType; } for (int i = 0; i < arrayType.getAnnotations().size(); i++) { this.visit(arrayType.getAnnotations().get(i), compareTo.getAnnotations().get(i)); if (!isEqual.get()) { return arrayType; } } this.visitTypeName(arrayType.getElementType(), compareTo.getElementType()); } return arrayType; } @Override public J.Assert visitAssert(J.Assert _assert, J j) { if (isEqual.get()) { if (!(j instanceof J.Assert)) { isEqual.set(false); return _assert; } J.Assert compareTo = (J.Assert) j; if (nullMissMatch(_assert.getDetail(), compareTo.getDetail()) || doesNotContainSameComments(_assert.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _assert; } this.visit(_assert.getCondition(), compareTo.getCondition()); if (_assert.getDetail() != null && compareTo.getDetail() != null) { this.visit(_assert.getDetail().getElement(), compareTo.getDetail().getElement()); } } return _assert; } @Override public J.Assignment visitAssignment(J.Assignment assignment, J j) { if (isEqual.get()) { if (!(j instanceof J.Assignment)) { isEqual.set(false); return assignment; } J.Assignment compareTo = (J.Assignment) j; if (nullMissMatch(assignment.getType(), compareTo.getType()) || !TypeUtils.isOfType(assignment.getType(), compareTo.getType()) || doesNotContainSameComments(assignment.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return assignment; } this.visit(assignment.getAssignment(), compareTo.getAssignment()); this.visit(assignment.getVariable(), compareTo.getVariable()); } return assignment; } @Override public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, J j) { if (isEqual.get()) { if (!(j instanceof J.AssignmentOperation)) { isEqual.set(false); return assignOp; } J.AssignmentOperation compareTo = (J.AssignmentOperation) j; if (nullMissMatch(assignOp.getType(), compareTo.getType()) || !TypeUtils.isOfType(assignOp.getType(), compareTo.getType()) || doesNotContainSameComments(assignOp.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return assignOp; } this.visit(assignOp.getAssignment(), compareTo.getAssignment()); this.visit(assignOp.getVariable(), compareTo.getVariable()); } return assignOp; } @Override public J.Binary visitBinary(J.Binary binary, J j) { if (isEqual.get()) { if (!(j instanceof J.Binary)) { isEqual.set(false); return binary; } J.Binary compareTo = (J.Binary) j; if (nullMissMatch(binary.getType(), compareTo.getType()) || !TypeUtils.isOfType(binary.getType(), compareTo.getType()) || binary.getOperator() != compareTo.getOperator() || doesNotContainSameComments(binary.getPadding().getOperator().getBefore(), compareTo.getPadding().getOperator().getBefore()) || doesNotContainSameComments(binary.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return binary; } this.visit(binary.getLeft(), compareTo.getLeft()); this.visit(binary.getRight(), compareTo.getRight()); } return binary; } @Override public J.Block visitBlock(J.Block block, J j) { if (isEqual.get()) { if (!(j instanceof J.Block)) { isEqual.set(false); return block; } J.Block compareTo = (J.Block) j; if (block.getStatements().size() != compareTo.getStatements().size() || doesNotContainSameComments(block.getPrefix(), compareTo.getPrefix()) || doesNotContainSameComments(block.getEnd(), compareTo.getEnd())) { isEqual.set(false); return block; } for (int i = 0; i < block.getStatements().size(); i++) { this.visit(block.getStatements().get(i), compareTo.getStatements().get(i)); } } return block; } @Override public J.Break visitBreak(J.Break breakStatement, J j) { if (isEqual.get()) { if (!(j instanceof J.Break)) { isEqual.set(false); return breakStatement; } J.Break compareTo = (J.Break) j; if (nullMissMatch(breakStatement.getLabel(), compareTo.getLabel()) || doesNotContainSameComments(breakStatement.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return breakStatement; } if (breakStatement.getLabel() != null && compareTo.getLabel() != null) { this.visit(breakStatement.getLabel(), compareTo.getLabel()); } } return breakStatement; } @Override public J.Case visitCase(J.Case _case, J j) { if (isEqual.get()) { if (!(j instanceof J.Case)) { isEqual.set(false); return _case; } J.Case compareTo = (J.Case) j; if (_case.getStatements().size() != compareTo.getStatements().size() || doesNotContainSameComments(_case.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _case; } this.visit(_case.getPattern(), compareTo.getPattern()); for (int i = 0; i < _case.getStatements().size(); i++) { this.visit(_case.getStatements().get(i), compareTo.getStatements().get(i)); } } return _case; } @Override public J.Try.Catch visitCatch(J.Try.Catch _catch, J j) { if (isEqual.get()) { if (!(j instanceof J.Try.Catch)) { isEqual.set(false); return _catch; } J.Try.Catch compareTo = (J.Try.Catch) j; if (doesNotContainSameComments(_catch.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _catch; } this.visit(_catch.getParameter(), compareTo.getParameter()); this.visit(_catch.getBody(), compareTo.getBody()); } return _catch; } @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, J j) { if (isEqual.get()) { if (!(j instanceof J.ClassDeclaration)) { isEqual.set(false); return classDecl; } J.ClassDeclaration compareTo = (J.ClassDeclaration) j; if (!TypeUtils.isOfType(classDecl.getType(), compareTo.getType()) || classDecl.getModifiers().size() != compareTo.getModifiers().size() || !new HashSet<>(classDecl.getModifiers()).containsAll(compareTo.getModifiers()) || classDecl.getKind() != compareTo.getKind() || classDecl.getLeadingAnnotations().size() != compareTo.getLeadingAnnotations().size() || nullMissMatch(classDecl.getExtends(), compareTo.getExtends()) || nullMissMatch(classDecl.getTypeParameters(), compareTo.getTypeParameters()) || classDecl.getTypeParameters() != null && compareTo.getTypeParameters() != null && classDecl.getTypeParameters().size() != compareTo.getTypeParameters().size() || nullMissMatch(classDecl.getImplements(), compareTo.getImplements()) || classDecl.getImplements() != null && compareTo.getImplements() != null && classDecl.getImplements().size() != compareTo.getImplements().size() || doesNotContainSameComments(classDecl.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return classDecl; } this.visit(classDecl.getName(), compareTo.getName()); for (int i = 0; i < classDecl.getLeadingAnnotations().size(); i++) { this.visit(classDecl.getLeadingAnnotations().get(i), compareTo.getLeadingAnnotations().get(i)); } if (classDecl.getExtends() != null && compareTo.getExtends() != null) { this.visit(classDecl.getExtends(), compareTo.getExtends()); } if (classDecl.getTypeParameters() != null && compareTo.getTypeParameters() != null) { for (int i = 0; i < classDecl.getTypeParameters().size(); i++) { this.visit(classDecl.getTypeParameters().get(i), compareTo.getTypeParameters().get(i)); } } if (classDecl.getImplements() != null && compareTo.getImplements() != null) { for (int i = 0; i < classDecl.getImplements().size(); i++) { this.visit(classDecl.getImplements().get(i), compareTo.getImplements().get(i)); } } this.visit(classDecl.getBody(), compareTo.getBody()); } return classDecl; } @Override public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, J j) { if (isEqual.get()) { if (!(j instanceof J.CompilationUnit)) { isEqual.set(false); return cu; } J.CompilationUnit compareTo = (J.CompilationUnit) j; if (nullMissMatch(cu.getPackageDeclaration(), compareTo.getPackageDeclaration()) || cu.getImports().size() != compareTo.getImports().size() || cu.getClasses().size() != compareTo.getClasses().size() || doesNotContainSameComments(cu.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return cu; } if (cu.getPackageDeclaration() != null && compareTo.getPackageDeclaration() != null) { this.visit(cu.getPackageDeclaration(), compareTo.getPackageDeclaration()); } for (int i = 0; i < cu.getImports().size(); i++) { this.visit(cu.getImports().get(i), compareTo.getImports().get(i)); } for (int i = 0; i < cu.getClasses().size(); i++) { this.visit(cu.getClasses().get(i), compareTo.getClasses().get(i)); } } return cu; } @SuppressWarnings("unchecked") @Override public J.ControlParentheses visitControlParentheses(J.ControlParentheses controlParens, J j) { if (isEqual.get()) { if (!(j instanceof J.ControlParentheses)) { isEqual.set(false); return controlParens; } J.ControlParentheses compareTo = (J.ControlParentheses) j; if (!TypeUtils.isOfType(controlParens.getType(), compareTo.getType()) || doesNotContainSameComments(controlParens.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return controlParens; } this.visit(controlParens.getTree(), compareTo.getTree()); } return controlParens; } @Override public J.Continue visitContinue(J.Continue continueStatement, J j) { if (isEqual.get()) { if (!(j instanceof J.Continue)) { isEqual.set(false); return continueStatement; } J.Continue compareTo = (J.Continue) j; if (nullMissMatch(continueStatement.getLabel(), compareTo.getLabel()) || doesNotContainSameComments(continueStatement.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return continueStatement; } if (continueStatement.getLabel() != null && compareTo.getLabel() != null) { this.visit(continueStatement.getLabel(), compareTo.getLabel()); } } return continueStatement; } @Override public J.DoWhileLoop visitDoWhileLoop(J.DoWhileLoop doWhileLoop, J j) { if (isEqual.get()) { if (!(j instanceof J.DoWhileLoop)) { isEqual.set(false); return doWhileLoop; } J.DoWhileLoop compareTo = (J.DoWhileLoop) j; if (doesNotContainSameComments(doWhileLoop.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return doWhileLoop; } this.visit(doWhileLoop.getWhileCondition(), compareTo.getWhileCondition()); this.visit(doWhileLoop.getBody(), compareTo.getBody()); } return doWhileLoop; } @Override public J.If.Else visitElse(J.If.Else else_, J j) { if (isEqual.get()) { if (!(j instanceof J.If.Else)) { isEqual.set(false); return else_; } J.If.Else compareTo = (J.If.Else) j; if (doesNotContainSameComments(else_.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return else_; } this.visit(else_.getBody(), compareTo.getBody()); } return else_; } @Override public J.Empty visitEmpty(J.Empty empty, J j) { if (isEqual.get()) { if (!(j instanceof J.Empty)) { isEqual.set(false); return empty; } J.Empty compareTo = (J.Empty) j; if (empty.getType() == null && compareTo.getType() != null || empty.getType() != null && compareTo.getType() == null || doesNotContainSameComments(empty.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return empty; } } return empty; } @Override public J.EnumValue visitEnumValue(J.EnumValue _enum, J j) { if (isEqual.get()) { if (!(j instanceof J.EnumValue)) { isEqual.set(false); return _enum; } J.EnumValue compareTo = (J.EnumValue) j; if (nullMissMatch(_enum.getAnnotations(), compareTo.getAnnotations()) || _enum.getAnnotations().size() != compareTo.getAnnotations().size() || doesNotContainSameComments(_enum.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _enum; } this.visit(_enum.getName(), compareTo.getName()); for (int i = 0; i < _enum.getAnnotations().size(); i++) { this.visit(_enum.getAnnotations().get(i), compareTo.getAnnotations().get(i)); } if (_enum.getInitializer() != null && compareTo.getInitializer() != null) { this.visit(_enum.getInitializer(), compareTo.getInitializer()); } } return _enum; } @Override public J.EnumValueSet visitEnumValueSet(J.EnumValueSet enums, J j) { if (isEqual.get()) { if (!(j instanceof J.EnumValueSet)) { isEqual.set(false); return enums; } J.EnumValueSet compareTo = (J.EnumValueSet) j; if (enums.getEnums().size() != compareTo.getEnums().size() || doesNotContainSameComments(enums.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return enums; } for (int i = 0; i < enums.getEnums().size(); i++) { this.visit(enums.getEnums().get(i), compareTo.getEnums().get(i)); } } return enums; } @Override public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, J j) { if (isEqual.get()) { if (!(j instanceof J.FieldAccess)) { isEqual.set(false); return fieldAccess; } J.FieldAccess compareTo = (J.FieldAccess) j; if (!TypeUtils.isOfType(fieldAccess.getType(), compareTo.getType()) || !TypeUtils.isOfType(fieldAccess.getTarget().getType(), compareTo.getTarget().getType()) || doesNotContainSameComments(fieldAccess.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return fieldAccess; } this.visit(fieldAccess.getName(), compareTo.getName()); } return fieldAccess; } @Override public J.ForEachLoop visitForEachLoop(J.ForEachLoop forLoop, J j) { if (isEqual.get()) { if (!(j instanceof J.ForEachLoop)) { isEqual.set(false); return forLoop; } J.ForEachLoop compareTo = (J.ForEachLoop) j; if (doesNotContainSameComments(forLoop.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return forLoop; } this.visit(forLoop.getControl(), compareTo.getControl()); this.visit(forLoop.getBody(), compareTo.getBody()); } return forLoop; } @Override public J.ForEachLoop.Control visitForEachControl(J.ForEachLoop.Control control, J j) { if (isEqual.get()) { if (!(j instanceof J.ForEachLoop.Control)) { isEqual.set(false); return control; } J.ForEachLoop.Control compareTo = (J.ForEachLoop.Control) j; if (doesNotContainSameComments(control.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return control; } this.visit(control.getVariable(), compareTo.getVariable()); this.visit(control.getIterable(), compareTo.getIterable()); } return control; } @Override public J.ForLoop visitForLoop(J.ForLoop forLoop, J j) { if (isEqual.get()) { if (!(j instanceof J.ForLoop)) { isEqual.set(false); return forLoop; } J.ForLoop compareTo = (J.ForLoop) j; if (doesNotContainSameComments(forLoop.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return forLoop; } this.visit(forLoop.getControl(), compareTo.getControl()); this.visit(forLoop.getBody(), compareTo.getBody()); } return forLoop; } @Override public J.ForLoop.Control visitForControl(J.ForLoop.Control control, J j) { if (isEqual.get()) { if (!(j instanceof J.ForLoop.Control)) { isEqual.set(false); return control; } J.ForLoop.Control compareTo = (J.ForLoop.Control) j; if (control.getInit().size() != compareTo.getInit().size() || control.getUpdate().size() != compareTo.getUpdate().size() || doesNotContainSameComments(control.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return control; } this.visit(control.getCondition(), compareTo.getCondition()); for (int i = 0; i < control.getInit().size(); i++) { this.visit(control.getInit().get(i), compareTo.getInit().get(i)); } for (int i = 0; i < control.getUpdate().size(); i++) { this.visit(control.getUpdate().get(i), compareTo.getUpdate().get(i)); } } return control; } @Override public J.Identifier visitIdentifier(J.Identifier identifier, J j) { if (isEqual.get()) { if (!(j instanceof J.Identifier)) { isEqual.set(false); return identifier; } J.Identifier compareTo = (J.Identifier) j; if (!identifier.getSimpleName().equals(compareTo.getSimpleName()) || !TypeUtils.isOfType(identifier.getType(), compareTo.getType()) || doesNotContainSameComments(identifier.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return identifier; } } return identifier; } @Override public J.If visitIf(J.If iff, J j) { if (isEqual.get()) { if (!(j instanceof J.If)) { isEqual.set(false); return iff; } J.If compareTo = (J.If) j; if (nullMissMatch(iff.getElsePart(), compareTo.getElsePart()) || doesNotContainSameComments(iff.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return iff; } this.visit(iff.getIfCondition(), compareTo.getIfCondition()); this.visit(iff.getThenPart(), compareTo.getThenPart()); if (iff.getElsePart() != null && compareTo.getElsePart() != null) { this.visit(iff.getElsePart(), compareTo.getElsePart()); } } return iff; } @Override public J.Import visitImport(J.Import _import, J j) { if (isEqual.get()) { if (!(j instanceof J.Import)) { isEqual.set(false); return _import; } J.Import compareTo = (J.Import) j; if (_import.isStatic() != compareTo.isStatic() || !_import.getPackageName().equals(compareTo.getPackageName()) || !_import.getClassName().equals(compareTo.getClassName()) || !TypeUtils.isOfType(_import.getQualid().getType(), compareTo.getQualid().getType())) { isEqual.set(false); return _import; } } return _import; } @Override public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, J j) { if (isEqual.get()) { if (!(j instanceof J.InstanceOf)) { isEqual.set(false); return instanceOf; } J.InstanceOf compareTo = (J.InstanceOf) j; if (!TypeUtils.isOfType(instanceOf.getType(), compareTo.getType()) || doesNotContainSameComments(instanceOf.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return instanceOf; } this.visit(instanceOf.getClazz(), compareTo.getClazz()); this.visit(instanceOf.getExpression(), compareTo.getExpression()); } return instanceOf; } @Override public J.Label visitLabel(J.Label label, J j) { if (isEqual.get()) { if (!(j instanceof J.Label)) { isEqual.set(false); return label; } J.Label compareTo = (J.Label) j; if (doesNotContainSameComments(label.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return label; } this.visit(label.getLabel(), compareTo.getLabel()); this.visit(label.getStatement(), compareTo.getStatement()); } return label; } @Override public J.Lambda visitLambda(J.Lambda lambda, J j) { if (isEqual.get()) { if (!(j instanceof J.Lambda)) { isEqual.set(false); return lambda; } J.Lambda compareTo = (J.Lambda) j; if (lambda.getParameters().isParenthesized() != compareTo.getParameters().isParenthesized() || lambda.getParameters().getParameters().size() != compareTo.getParameters().getParameters().size() || doesNotContainSameComments(lambda.getPrefix(), compareTo.getPrefix()) || doesNotContainSameComments(lambda.getArrow(), compareTo.getArrow())) { isEqual.set(false); return lambda; } this.visit(lambda.getBody(), compareTo.getBody()); for (int i = 0; i < lambda.getParameters().getParameters().size(); i++) { this.visit(lambda.getParameters().getParameters().get(i), compareTo.getParameters().getParameters().get(i)); } } return lambda; } @Override public J.Literal visitLiteral(J.Literal literal, J j) { if (isEqual.get()) { if (!(j instanceof J.Literal)) { isEqual.set(false); return literal; } J.Literal compareTo = (J.Literal) j; if (!TypeUtils.isOfType(literal.getType(), compareTo.getType()) || !Objects.equals(literal.getValue(), compareTo.getValue()) || doesNotContainSameComments(literal.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return literal; } } return literal; } @Override public J.MemberReference visitMemberReference(J.MemberReference memberRef, J j) { if (isEqual.get()) { if (!(j instanceof J.MemberReference)) { isEqual.set(false); return memberRef; } J.MemberReference compareTo = (J.MemberReference) j; if (!TypeUtils.isOfType(memberRef.getType(), compareTo.getType()) || !TypeUtils.isOfType(memberRef.getVariableType(), compareTo.getVariableType()) || !TypeUtils.isOfType(memberRef.getMethodType(), compareTo.getMethodType()) || nullMissMatch(memberRef.getTypeParameters(), compareTo.getTypeParameters()) || memberRef.getTypeParameters() != null && compareTo.getTypeParameters() != null && memberRef.getTypeParameters().size() != compareTo.getTypeParameters().size() || doesNotContainSameComments(memberRef.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return memberRef; } this.visit(memberRef.getReference(), compareTo.getReference()); this.visit(memberRef.getContaining(), compareTo.getContaining()); if (memberRef.getTypeParameters() != null && compareTo.getTypeParameters() != null) { for (int i = 0; i < memberRef.getTypeParameters().size(); i++) { this.visit(memberRef.getTypeParameters().get(i), compareTo.getTypeParameters().get(i)); } } } return memberRef; } @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, J j) { if (isEqual.get()) { if (!(j instanceof J.MethodDeclaration)) { isEqual.set(false); return method; } J.MethodDeclaration compareTo = (J.MethodDeclaration) j; if (!TypeUtils.isOfType(method.getMethodType(), compareTo.getMethodType()) || method.getModifiers().size() != compareTo.getModifiers().size() || !new HashSet<>(method.getModifiers()).containsAll(compareTo.getModifiers()) || method.getLeadingAnnotations().size() != compareTo.getLeadingAnnotations().size() || method.getParameters().size() != compareTo.getParameters().size() || nullMissMatch(method.getReturnTypeExpression(), compareTo.getReturnTypeExpression()) || nullMissMatch(method.getTypeParameters(), compareTo.getTypeParameters()) || method.getTypeParameters() != null && compareTo.getTypeParameters() != null && method.getTypeParameters().size() != compareTo.getTypeParameters().size() || nullMissMatch(method.getThrows(), compareTo.getThrows()) || method.getThrows() != null && compareTo.getThrows() != null && method.getThrows().size() != compareTo.getThrows().size() || nullMissMatch(method.getBody(), compareTo.getBody()) || method.getBody().getStatements() != null && compareTo.getBody().getStatements() != null && method.getBody().getStatements().size() != compareTo.getBody().getStatements().size() || doesNotContainSameComments(method.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return method; } this.visit(method.getName(), compareTo.getName()); for (int i = 0; i < method.getLeadingAnnotations().size(); i++) { this.visit(method.getLeadingAnnotations().get(i), compareTo.getLeadingAnnotations().get(i)); } for (int i = 0; i < method.getParameters().size(); i++) { this.visit(method.getParameters().get(i), compareTo.getParameters().get(i)); } if (method.getReturnTypeExpression() != null && compareTo.getReturnTypeExpression() != null) { this.visitTypeName(method.getReturnTypeExpression(), compareTo.getReturnTypeExpression()); } if (method.getTypeParameters() != null && compareTo.getTypeParameters() != null) { for (int i = 0; i < method.getTypeParameters().size(); i++) { this.visit(method.getTypeParameters().get(i), compareTo.getTypeParameters().get(i)); } } if (method.getThrows() != null && compareTo.getThrows() != null) { for (int i = 0; i < method.getThrows().size(); i++) { this.visitTypeName(method.getThrows().get(i), compareTo.getThrows().get(i)); } } if (method.getBody() != null && compareTo.getBody() != null) { this.visit(method.getBody(), compareTo.getBody()); } } return method; } @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, J j) { if (isEqual.get()) { if (!(j instanceof J.MethodInvocation)) { isEqual.set(false); return method; } J.MethodInvocation compareTo = (J.MethodInvocation) j; if (!TypeUtils.isOfType(method.getMethodType(), compareTo.getMethodType()) || nullMissMatch(method.getSelect(), compareTo.getSelect()) || method.getArguments().size() != compareTo.getArguments().size() || method.getTypeParameters() != null && compareTo.getTypeParameters() != null && method.getTypeParameters().size() != compareTo.getTypeParameters().size() || doesNotContainSameComments(method.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return method; } this.visit(method.getName(), compareTo.getName()); this.visit(method.getSelect(), compareTo.getSelect()); boolean containsLiteral = false; if (!compareMethodArguments) { for (int i = 0; i < method.getArguments().size(); i++) { if (method.getArguments().get(i) instanceof J.Literal || compareTo.getArguments().get(i) instanceof J.Literal) { containsLiteral = true; break; } } if (!containsLiteral) { if (nullMissMatch(method.getMethodType(), compareTo.getMethodType()) || !TypeUtils.isOfType(method.getMethodType(), compareTo.getMethodType())) { isEqual.set(false); return method; } } } if (compareMethodArguments || containsLiteral) { for (int i = 0; i < method.getArguments().size(); i++) { this.visit(method.getArguments().get(i), compareTo.getArguments().get(i)); } } if (method.getTypeParameters() != null && compareTo.getTypeParameters() != null) { for (int i = 0; i < method.getTypeParameters().size(); i++) { this.visit(method.getTypeParameters().get(i), compareTo.getTypeParameters().get(i)); } } } return method; } @Override public J.MultiCatch visitMultiCatch(J.MultiCatch multiCatch, J j) { if (isEqual.get()) { if (!(j instanceof J.MultiCatch)) { isEqual.set(false); return multiCatch; } J.MultiCatch compareTo = (J.MultiCatch) j; if (!(multiCatch.getType() instanceof JavaType.MultiCatch) || !(compareTo.getType() instanceof JavaType.MultiCatch) || ((JavaType.MultiCatch) multiCatch.getType()).getThrowableTypes().size() != ((JavaType.MultiCatch) compareTo.getType()).getThrowableTypes().size() || multiCatch.getAlternatives().size() != compareTo.getAlternatives().size() || doesNotContainSameComments(multiCatch.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return multiCatch; } for (int i = 0; i < ((JavaType.MultiCatch) multiCatch.getType()).getThrowableTypes().size(); i++) { JavaType first = ((JavaType.MultiCatch) multiCatch.getType()).getThrowableTypes().get(i); JavaType second = ((JavaType.MultiCatch) compareTo.getType()).getThrowableTypes().get(i); if (!TypeUtils.isOfType(first, second)) { isEqual.set(false); return multiCatch; } } for (int i = 0; i < multiCatch.getAlternatives().size(); i++) { this.visit(multiCatch.getAlternatives().get(i), compareTo.getAlternatives().get(i)); } } return multiCatch; } @Override public J.NewArray visitNewArray(J.NewArray newArray, J j) { if (isEqual.get()) { if (!(j instanceof J.NewArray)) { isEqual.set(false); return newArray; } J.NewArray compareTo = (J.NewArray) j; if (!TypeUtils.isOfType(newArray.getType(), compareTo.getType()) || newArray.getDimensions().size() != compareTo.getDimensions().size() || nullMissMatch(newArray.getTypeExpression(), compareTo.getTypeExpression()) || nullMissMatch(newArray.getInitializer(), compareTo.getInitializer()) || newArray.getInitializer() != null && compareTo.getInitializer() != null && newArray.getInitializer().size() != compareTo.getInitializer().size() || doesNotContainSameComments(newArray.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return newArray; } for (int i = 0; i < newArray.getDimensions().size(); i++) { this.visit(newArray.getDimensions().get(i), compareTo.getDimensions().get(i)); } if (newArray.getTypeExpression() != null && compareTo.getTypeExpression() != null) { this.visit(newArray.getTypeExpression(), compareTo.getTypeExpression()); } if (newArray.getInitializer() != null && compareTo.getInitializer() != null) { for (int i = 0; i < newArray.getInitializer().size(); i++) { this.visit(newArray.getInitializer().get(i), compareTo.getInitializer().get(i)); } } } return newArray; } @Override public J.NewClass visitNewClass(J.NewClass newClass, J j) { if (isEqual.get()) { if (!(j instanceof J.NewClass)) { isEqual.set(false); return newClass; } J.NewClass compareTo = (J.NewClass) j; if (!TypeUtils.isOfType(newClass.getType(), compareTo.getType()) || !TypeUtils.isOfType(newClass.getConstructorType(), compareTo.getConstructorType()) || nullMissMatch(newClass.getEnclosing(), compareTo.getEnclosing()) || nullMissMatch(newClass.getClazz(), compareTo.getClazz()) || nullMissMatch(newClass.getConstructorType(), compareTo.getConstructorType()) || nullMissMatch(newClass.getBody(), compareTo.getBody()) || nullMissMatch(newClass.getArguments(), compareTo.getArguments()) || newClass.getArguments().size() != compareTo.getArguments().size() || doesNotContainSameComments(newClass.getPrefix(), compareTo.getPrefix()) || doesNotContainSameComments(newClass.getNew(), compareTo.getNew())) { isEqual.set(false); return newClass; } if (newClass.getEnclosing() != null && compareTo.getEnclosing() != null) { this.visit(newClass.getEnclosing(), compareTo.getEnclosing()); } if (newClass.getClazz() != null && compareTo.getClazz() != null) { this.visit(newClass.getClazz(), compareTo.getClazz()); } if (newClass.getBody() != null && compareTo.getBody() != null) { this.visit(newClass.getBody(), compareTo.getBody()); } boolean containsLiteral = false; if (!compareMethodArguments) { for (int i = 0; i < newClass.getArguments().size(); i++) { if (newClass.getArguments().get(i) instanceof J.Literal || compareTo.getArguments().get(i) instanceof J.Literal) { containsLiteral = true; break; } } if (!containsLiteral) { if (nullMissMatch(newClass.getConstructorType(), compareTo.getConstructorType()) || newClass.getConstructorType() != null && compareTo.getConstructorType() != null && !TypeUtils.isOfType(newClass.getConstructorType(), compareTo.getConstructorType())) { isEqual.set(false); return newClass; } } } if (compareMethodArguments || containsLiteral) { for (int i = 0; i < newClass.getArguments().size(); i++) { this.visit(newClass.getArguments().get(i), compareTo.getArguments().get(i)); } } } return newClass; } @Override public J.Package visitPackage(J.Package pkg, J j) { if (isEqual.get()) { if (!(j instanceof J.Package)) { isEqual.set(false); return pkg; } J.Package compareTo = (J.Package) j; if (pkg.getAnnotations().size() != compareTo.getAnnotations().size() || !pkg.getExpression().toString().equals(compareTo.getExpression().toString())) { isEqual.set(false); return pkg; } for (int i = 0; i < pkg.getAnnotations().size(); i++) { this.visit(pkg.getAnnotations().get(i), compareTo.getAnnotations().get(i)); } } return pkg; } @Override public J.ParameterizedType visitParameterizedType(J.ParameterizedType type, J j) { if (isEqual.get()) { if (!(j instanceof J.ParameterizedType)) { isEqual.set(false); return type; } J.ParameterizedType compareTo = (J.ParameterizedType) j; if (!TypeUtils.isOfType(type.getType(), compareTo.getType()) || nullMissMatch(type.getTypeParameters(), compareTo.getTypeParameters()) || type.getTypeParameters() != null && compareTo.getTypeParameters() != null && type.getTypeParameters().size() != compareTo.getTypeParameters().size() || doesNotContainSameComments(type.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return type; } if (type.getTypeParameters() != null && compareTo.getTypeParameters() != null) { for (int i = 0; i < type.getTypeParameters().size(); i++) { this.visit(type.getTypeParameters().get(i), compareTo.getTypeParameters().get(i)); } } } return type; } @SuppressWarnings("unchecked") @Override public J.Parentheses visitParentheses(J.Parentheses parens, J j) { if (isEqual.get()) { if (!(j instanceof J.Parentheses)) { isEqual.set(false); return parens; } J.Parentheses compareTo = (J.Parentheses) j; if (doesNotContainSameComments(parens.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return parens; } this.visit(parens.getTree(), compareTo.getTree()); } return parens; } @Override public J.Primitive visitPrimitive(J.Primitive primitive, J j) { if (isEqual.get()) { if (!(j instanceof J.Primitive)) { isEqual.set(false); return primitive; } J.Primitive compareTo = (J.Primitive) j; if (!TypeUtils.isOfType(primitive.getType(), compareTo.getType()) || doesNotContainSameComments(primitive.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return primitive; } } return primitive; } @Override public J.Return visitReturn(J.Return _return, J j) { if (isEqual.get()) { if (!(j instanceof J.Return)) { isEqual.set(false); return _return; } J.Return compareTo = (J.Return) j; if (nullMissMatch(_return.getExpression(), compareTo.getExpression()) || doesNotContainSameComments(_return.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _return; } if (_return.getExpression() != null && compareTo.getExpression() != null) { this.visit(_return.getExpression(), compareTo.getExpression()); } } return _return; } @Override public J.Switch visitSwitch(J.Switch _switch, J j) { if (isEqual.get()) { if (!(j instanceof J.Switch)) { isEqual.set(false); return _switch; } J.Switch compareTo = (J.Switch) j; if (doesNotContainSameComments(_switch.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _switch; } this.visit(_switch.getCases(), compareTo.getCases()); } return _switch; } @Override public J.Synchronized visitSynchronized(J.Synchronized _sync, J j) { if (isEqual.get()) { if (!(j instanceof J.Synchronized)) { isEqual.set(false); return _sync; } J.Synchronized compareTo = (J.Synchronized) j; if (doesNotContainSameComments(_sync.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _sync; } this.visit(_sync.getLock(), compareTo.getLock()); this.visit(_sync.getBody(), compareTo.getBody()); } return _sync; } @Override public J.Ternary visitTernary(J.Ternary ternary, J j) { if (isEqual.get()) { if (!(j instanceof J.Ternary)) { isEqual.set(false); return ternary; } J.Ternary compareTo = (J.Ternary) j; if (!TypeUtils.isOfType(ternary.getType(), compareTo.getType()) || doesNotContainSameComments(ternary.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return ternary; } this.visit(ternary.getCondition(), compareTo.getCondition()); this.visit(ternary.getTruePart(), compareTo.getTruePart()); this.visit(ternary.getFalsePart(), compareTo.getFalsePart()); } return ternary; } @Override public J.Throw visitThrow(J.Throw thrown, J j) { if (isEqual.get()) { if (!(j instanceof J.Throw)) { isEqual.set(false); return thrown; } J.Throw compareTo = (J.Throw) j; if (doesNotContainSameComments(thrown.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return thrown; } this.visit(thrown.getException(), compareTo.getException()); } return thrown; } @Override public J.Try visitTry(J.Try _try, J j) { if (isEqual.get()) { if (!(j instanceof J.Try)) { isEqual.set(false); return _try; } J.Try compareTo = (J.Try) j; if (_try.getCatches().size() != compareTo.getCatches().size() || nullMissMatch(_try.getFinally(), compareTo.getFinally()) || nullMissMatch(_try.getResources(), compareTo.getResources()) || _try.getResources() != null && compareTo.getResources() != null && _try.getResources().size() != compareTo.getResources().size() || doesNotContainSameComments(_try.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return _try; } this.visit(_try.getBody(), compareTo.getBody()); for (int i = 0; i < _try.getCatches().size(); i++) { this.visit(_try.getCatches().get(i), compareTo.getCatches().get(i)); } if (_try.getResources() != null && compareTo.getResources() != null) { for (int i = 0; i < _try.getResources().size(); i++) { this.visit(_try.getResources().get(i), compareTo.getResources().get(i)); } } if (_try.getFinally() != null && compareTo.getFinally() != null) { this.visit(_try.getFinally(), compareTo.getFinally()); } } return _try; } @Override public J.Try.Resource visitTryResource(J.Try.Resource tryResource, J j) { if (isEqual.get()) { if (!(j instanceof J.Try.Resource)) { isEqual.set(false); return tryResource; } J.Try.Resource compareTo = (J.Try.Resource) j; if (tryResource.isTerminatedWithSemicolon() != compareTo.isTerminatedWithSemicolon() || doesNotContainSameComments(tryResource.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return tryResource; } this.visit(tryResource.getVariableDeclarations(), compareTo.getVariableDeclarations()); } return tryResource; } @Override public J.TypeCast visitTypeCast(J.TypeCast typeCast, J j) { if (isEqual.get()) { if (!(j instanceof J.TypeCast)) { isEqual.set(false); return typeCast; } J.TypeCast compareTo = (J.TypeCast) j; if (doesNotContainSameComments(typeCast.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return typeCast; } this.visit(typeCast.getClazz(), compareTo.getClazz()); this.visit(typeCast.getExpression(), compareTo.getExpression()); } return typeCast; } @Override public J.TypeParameter visitTypeParameter(J.TypeParameter typeParam, J j) { if (isEqual.get()) { if (!(j instanceof J.TypeParameter)) { isEqual.set(false); return typeParam; } J.TypeParameter compareTo = (J.TypeParameter) j; if (typeParam.getAnnotations().size() != compareTo.getAnnotations().size() || nullMissMatch(typeParam.getBounds(), compareTo.getBounds()) || typeParam.getBounds().size() != compareTo.getBounds().size() || doesNotContainSameComments(typeParam.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return typeParam; } this.visit(typeParam.getName(), compareTo.getName()); for (int i = 0; i < typeParam.getAnnotations().size(); i++) { this.visit(typeParam.getAnnotations().get(i), compareTo.getAnnotations().get(i)); } if (typeParam.getBounds() != null && compareTo.getBounds() != null) { for (int i = 0; i < typeParam.getBounds().size(); i++) { this.visit(typeParam.getBounds().get(i), compareTo.getBounds().get(i)); } } } return typeParam; } @Override public J.Unary visitUnary(J.Unary unary, J j) { if (isEqual.get()) { if (!(j instanceof J.Unary)) { isEqual.set(false); return unary; } J.Unary compareTo = (J.Unary) j; if (nullMissMatch(unary.getType(), compareTo.getType()) || !TypeUtils.isOfType(unary.getType(), compareTo.getType()) || unary.getOperator() != compareTo.getOperator() || doesNotContainSameComments(unary.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return unary; } this.visit(unary.getExpression(), compareTo.getExpression()); } return unary; } @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, J j) { if (isEqual.get()) { if (!(j instanceof J.VariableDeclarations)) { isEqual.set(false); return multiVariable; } J.VariableDeclarations compareTo = (J.VariableDeclarations) j; if (!TypeUtils.isOfType(multiVariable.getType(), compareTo.getType()) || nullMissMatch(multiVariable.getTypeExpression(), compareTo.getTypeExpression()) || multiVariable.getVariables().size() != compareTo.getVariables().size() || multiVariable.getLeadingAnnotations().size() != compareTo.getLeadingAnnotations().size() || doesNotContainSameComments(multiVariable.getPrefix(), compareTo.getPrefix()) || doesNotContainSameComments(multiVariable.getVarargs(), compareTo.getVarargs())) { isEqual.set(false); return multiVariable; } if (multiVariable.getTypeExpression() != null && compareTo.getTypeExpression() != null) { this.visitTypeName(multiVariable.getTypeExpression(), compareTo.getTypeExpression()); } for (int i = 0; i < multiVariable.getLeadingAnnotations().size(); i++) { this.visit(multiVariable.getLeadingAnnotations().get(i), compareTo.getLeadingAnnotations().get(i)); } for (int i = 0; i < multiVariable.getVariables().size(); i++) { this.visit(multiVariable.getVariables().get(i), compareTo.getVariables().get(i)); } } return multiVariable; } @Override public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, J j) { if (isEqual.get()) { if (!(j instanceof J.VariableDeclarations.NamedVariable)) { isEqual.set(false); return variable; } J.VariableDeclarations.NamedVariable compareTo = (J.VariableDeclarations.NamedVariable) j; if (!TypeUtils.isOfType(variable.getType(), compareTo.getType()) || nullMissMatch(variable.getInitializer(), compareTo.getInitializer()) || doesNotContainSameComments(variable.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return variable; } this.visit(variable.getName(), compareTo.getName()); if (variable.getInitializer() != null && compareTo.getInitializer() != null) { this.visit(variable.getInitializer(), compareTo.getInitializer()); } } return variable; } @Override public J.WhileLoop visitWhileLoop(J.WhileLoop whileLoop, J j) { if (isEqual.get()) { if (!(j instanceof J.WhileLoop)) { isEqual.set(false); return whileLoop; } J.WhileLoop compareTo = (J.WhileLoop) j; if (doesNotContainSameComments(whileLoop.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return whileLoop; } this.visit(whileLoop.getBody(), compareTo.getBody()); this.visit(whileLoop.getCondition(), compareTo.getCondition()); } return whileLoop; } @Override public J.Wildcard visitWildcard(J.Wildcard wildcard, J j) { if (isEqual.get()) { if (!(j instanceof J.Wildcard)) { isEqual.set(false); return wildcard; } J.Wildcard compareTo = (J.Wildcard) j; if (wildcard.getBound() != compareTo.getBound() || nullMissMatch(wildcard.getBoundedType(), compareTo.getBoundedType()) || doesNotContainSameComments(wildcard.getPrefix(), compareTo.getPrefix())) { isEqual.set(false); return wildcard; } if (wildcard.getBoundedType() != null && compareTo.getBoundedType() != null) { this.visitTypeName(wildcard.getBoundedType(), compareTo.getBoundedType()); } } return wildcard; } @Override public N visitTypeName(N firstTypeName, J j) { if (isEqual.get()) { if (!(j instanceof NameTree) && !TypeUtils.isOfType(firstTypeName.getType(), ((NameTree) j).getType()) || doesNotContainSameComments(firstTypeName.getPrefix(), j.getPrefix())) { isEqual.set(false); return firstTypeName; } } return firstTypeName; } } /** * Collection the caught exceptions from a {@link J.Try.Catch}. */ private static Set getCaughtExceptions(J.Try.Catch aCatch) { Set caughtExceptions = new HashSet<>(); if (isMultiCatch(aCatch)) { J.MultiCatch multiCatch = (J.MultiCatch) aCatch.getParameter().getTree().getTypeExpression(); if (multiCatch != null) { for (NameTree alternative : multiCatch.getAlternatives()) { J.Identifier identifier = (J.Identifier) alternative; caughtExceptions.add(identifier); } } } else { J.Identifier identifier = (J.Identifier) aCatch.getParameter().getTree().getTypeExpression(); if (identifier != null) { caughtExceptions.add(identifier); } } return caughtExceptions; } /** * Returns true of a {@link J.Try.Catch} is a {@link J.MultiCatch}. * Note: A null type expression will produce a false negative, but the recipe will not * change catches with a null type. */ private static boolean isMultiCatch(J.Try.Catch aCatch) { return aCatch.getParameter().getTree().getTypeExpression() instanceof J.MultiCatch; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy