| /* |
| * Copyright 2007-2008 Dave Griffith |
| * |
| * 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 |
| * |
| * http://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.jetbrains.plugins.groovy.codeInspection.utils; |
| |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiMethod; |
| import com.intellij.psi.PsiType; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseLabel; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrForClause; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| |
| @SuppressWarnings({"OverlyComplexMethod", |
| "MethodWithMultipleLoops", |
| "OverlyComplexMethod", |
| "OverlyLongMethod", |
| "SwitchStatementWithTooManyBranches", |
| "SwitchStatement", |
| "OverlyComplexClass", |
| "ClassWithTooManyMethods"}) |
| public class EquivalenceChecker { |
| |
| private EquivalenceChecker() { |
| super(); |
| } |
| |
| private static final int LITERAL_EXPRESSION = 1; |
| private static final int REFERENCE_EXPRESSION = 3; |
| private static final int CALL_EXPRESSION = 5; |
| private static final int NEW_EXPRESSION = 6; |
| private static final int ARRAY_LITERAL_EXPRESSION = 7; |
| private static final int CLOSABLE_BLOCK_EXPRESSION = 8; |
| private static final int PREFIX_EXPRESSION = 10; |
| private static final int POSTFIX_EXPRESSION = 11; |
| private static final int BINARY_EXPRESSION = 12; |
| private static final int CONDITIONAL_EXPRESSION = 13; |
| private static final int ASSIGNMENT_EXPRESSION = 14; |
| private static final int ELVIS_EXPRESSION = 15; |
| private static final int TYPE_CAST_EXPRESSION = 16; |
| private static final int SAFE_CAST_EXPRESSION = 17; |
| private static final int INSTANCEOF_EXPRESSION = 18; |
| private static final int RANGE_EXPRESSION = 19; |
| private static final int LIST_OR_MAP_EXPRESSION = 20; |
| private static final int INDEX_EXPRESSION = 21; |
| private static final int PROPERTY_SELECTION_EXPRESSION = 22; |
| |
| private static final int BLOCK_STATEMENT = 1; |
| private static final int BREAK_STATEMENT = 2; |
| private static final int CONTINUE_STATEMENT = 3; |
| private static final int VAR_STATEMENT = 4; |
| private static final int EMPTY_STATEMENT = 6; |
| private static final int EXPRESSION_STATEMENT = 8; |
| private static final int FOR_STATEMENT = 9; |
| private static final int IF_STATEMENT = 10; |
| private static final int RETURN_STATEMENT = 12; |
| private static final int SWITCH_STATEMENT = 14; |
| private static final int THROW_STATEMENT = 16; |
| private static final int TRY_STATEMENT = 17; |
| private static final int WHILE_STATEMENT = 18; |
| private static final int SYNCHRONIZED_STATEMENT = 19; |
| private static final int ASSERT_STATEMENT = 20; |
| private static final int APPLICATION_STATEMENT = 21; |
| |
| public static boolean statementsAreEquivalent(@Nullable GrStatement exp1, |
| @Nullable GrStatement exp2) { |
| if (exp1 == null && exp2 == null) { |
| return true; |
| } |
| if (exp1 == null || exp2 == null) { |
| return false; |
| } |
| final int type1 = getStatementType(exp1); |
| final int type2 = getStatementType(exp2); |
| if (type1 != type2) { |
| return false; |
| } |
| switch (type1) { |
| case BLOCK_STATEMENT: |
| return blockStatementsAreEquivalent((GrBlockStatement) exp1, (GrBlockStatement) exp2); |
| case BREAK_STATEMENT: |
| return true; |
| case CONTINUE_STATEMENT: |
| return true; |
| case VAR_STATEMENT: |
| return varStatementsAreEquivalent((GrVariableDeclaration) exp1, (GrVariableDeclaration) exp2); |
| case EMPTY_STATEMENT: |
| return true; |
| case APPLICATION_STATEMENT: |
| return applicationStatementsAreEquivalent((GrApplicationStatement) exp1, (GrApplicationStatement) exp2); |
| case EXPRESSION_STATEMENT: |
| return expressionStatementsAreEquivalent((GrExpression) exp1, (GrExpression) exp2); |
| case FOR_STATEMENT: |
| return forInStatementsAreEquivalent((GrForStatement) exp1, (GrForStatement) exp2); |
| case IF_STATEMENT: |
| return ifStatementsAreEquivalent((GrIfStatement) exp1, (GrIfStatement) exp2); |
| case RETURN_STATEMENT: |
| return returnStatementsAreEquivalent((GrReturnStatement) exp1, (GrReturnStatement) exp2); |
| case SWITCH_STATEMENT: |
| return switchStatementsAreEquivalent((GrSwitchStatement) exp1, (GrSwitchStatement) exp2); |
| case THROW_STATEMENT: |
| return throwStatementsAreEquivalent((GrThrowStatement) exp1, (GrThrowStatement) exp2); |
| case TRY_STATEMENT: |
| return tryStatementsAreEquivalent((GrTryCatchStatement) exp1, (GrTryCatchStatement) exp2); |
| case WHILE_STATEMENT: |
| return whileStatementsAreEquivalent((GrWhileStatement) exp1, (GrWhileStatement) exp2); |
| case SYNCHRONIZED_STATEMENT: |
| return synchronizedStatementsAreEquivalent((GrSynchronizedStatement) exp1, (GrSynchronizedStatement) exp2); |
| case ASSERT_STATEMENT: |
| return assertStatementsAreEquivalent((GrAssertStatement) exp1, (GrAssertStatement) exp2); |
| default: |
| return false; |
| } |
| } |
| |
| private static boolean applicationStatementsAreEquivalent(GrApplicationStatement statement1, |
| GrApplicationStatement statement2) { |
| final GrExpression funExpression1 = statement1.getInvokedExpression(); |
| final GrExpression funExpression2 = statement2.getInvokedExpression(); |
| if (!expressionsAreEquivalent(funExpression1, funExpression2)) { |
| return false; |
| } |
| |
| final GrArgumentList argumentList1 = statement1.getArgumentList(); |
| if (argumentList1 == null) { |
| return false; |
| } |
| final GrArgumentList argumentList2 = statement2.getArgumentList(); |
| if (argumentList2 == null) { |
| return false; |
| } |
| final GrExpression[] args1 = argumentList1.getExpressionArguments(); |
| final GrExpression[] args2 = argumentList2.getExpressionArguments(); |
| if (!expressionListsAreEquivalent(args1, args2)) { |
| return false; |
| } |
| final GrNamedArgument[] namedArgs1 = argumentList1.getNamedArguments(); |
| final GrNamedArgument[] namedArgs2 = argumentList2.getNamedArguments(); |
| if (!namedArgumentListsAreEquivalent(namedArgs1, namedArgs2)) { |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean assertStatementsAreEquivalent(GrAssertStatement statement1, GrAssertStatement statement2) { |
| return expressionsAreEquivalent(statement1.getAssertion(), statement2.getAssertion()) && |
| expressionsAreEquivalent(statement1.getErrorMessage(), statement2.getErrorMessage()); |
| } |
| |
| private static boolean synchronizedStatementsAreEquivalent(GrSynchronizedStatement statement1, |
| GrSynchronizedStatement statement2) { |
| return expressionsAreEquivalent(statement1.getMonitor(), statement2.getMonitor()) && |
| openBlocksAreEquivalent(statement1.getBody(), statement2.getBody()); |
| } |
| |
| private static boolean varStatementsAreEquivalent(@NotNull GrVariableDeclaration statement1, |
| @NotNull GrVariableDeclaration statement2) { |
| final GrVariable[] variables1 = statement1.getVariables(); |
| final GrVariable[] variables2 = statement2.getVariables(); |
| if (variables1.length != variables2.length) { |
| return false; |
| } |
| for (int i = 0; i < variables2.length; i++) { |
| if (!variablesAreEquivalent(variables1[i], variables2[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean variablesAreEquivalent(@NotNull GrVariable var1, |
| @NotNull GrVariable var2) { |
| final GrExpression initializer1 = (GrExpression) var1.getInitializer(); |
| final GrExpression initializer2 = (GrExpression) var2.getInitializer(); |
| if (!expressionsAreEquivalent(initializer1, initializer2)) { |
| return false; |
| } |
| final PsiType type1 = var1.getType(); |
| final PsiType type2 = var2.getType(); |
| if (!typesAreEquivalent(type1, type2)) { |
| return false; |
| } |
| final String name1 = var1.getName(); |
| final String name2 = var2.getName(); |
| return name1.equals(name2); |
| } |
| |
| private static boolean tryStatementsAreEquivalent(@NotNull GrTryCatchStatement statement1, |
| @NotNull GrTryCatchStatement statement2) { |
| final GrOpenBlock tryBlock1 = statement1.getTryBlock(); |
| final GrOpenBlock tryBlock2 = statement2.getTryBlock(); |
| if (!openBlocksAreEquivalent(tryBlock1, tryBlock2)) { |
| return false; |
| } |
| final GrFinallyClause finallyBlock1 = statement1.getFinallyClause(); |
| final GrFinallyClause finallyBlock2 = statement2.getFinallyClause(); |
| if (finallyBlock1 != null) { |
| if (finallyBlock2 == null || !openBlocksAreEquivalent(finallyBlock1.getBody(), finallyBlock2.getBody())) { |
| return false; |
| } |
| } else if (finallyBlock2 != null) { |
| return false; |
| } |
| final GrCatchClause[] catchBlocks1 = statement1.getCatchClauses(); |
| final GrCatchClause[] catchBlocks2 = statement2.getCatchClauses(); |
| if (catchBlocks1.length != catchBlocks2.length) { |
| return false; |
| } |
| for (int i = 0; i < catchBlocks2.length; i++) { |
| if (!catchClausesAreEquivalent(catchBlocks1[i], catchBlocks2[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean catchClausesAreEquivalent(GrCatchClause clause1, GrCatchClause clause2) { |
| return parametersAreEquivalent(clause1.getParameter(), clause2.getParameter()) && |
| openBlocksAreEquivalent(clause1.getBody(), clause2.getBody()); |
| } |
| |
| private static boolean parametersAreEquivalent(@Nullable GrParameter parameter1, |
| @Nullable GrParameter parameter2) { |
| if (parameter1 == null || parameter2 == null) { |
| return false; |
| } |
| final PsiType type1 = parameter1.getType(); |
| final PsiType type2 = parameter2.getType(); |
| if (!typesAreEquivalent(type1, type2)) { |
| return false; |
| } |
| final String name1 = parameter1.getName(); |
| final String name2 = parameter2.getName(); |
| return name1.equals(name2); |
| } |
| |
| private static boolean typesAreEquivalent(@Nullable PsiType type1, @Nullable PsiType type2) { |
| if (type1 == null) { |
| return type2 == null; |
| } |
| if (type2 == null) { |
| return false; |
| } |
| return type1.equals(type2); |
| } |
| |
| private static boolean whileStatementsAreEquivalent(@NotNull GrWhileStatement statement1, |
| @NotNull GrWhileStatement statement2) { |
| final GrExpression condition1 = (GrExpression) statement1.getCondition(); |
| final GrExpression condition2 = (GrExpression) statement2.getCondition(); |
| final GrStatement body1 = statement1.getBody(); |
| final GrStatement body2 = statement2.getBody(); |
| return expressionsAreEquivalent(condition1, condition2) && |
| statementsAreEquivalent(body1, body2); |
| } |
| |
| private static boolean forInStatementsAreEquivalent(@NotNull GrForStatement statement1, |
| @NotNull GrForStatement statement2) { |
| final GrForClause clause1 = statement1.getClause(); |
| final GrForClause clause2 = statement2.getClause(); |
| if (!forClausesAreEquivalent(clause1, clause2)) { |
| return false; |
| } |
| final GrStatement body1 = statement1.getBody(); |
| final GrStatement body2 = statement2.getBody(); |
| return statementsAreEquivalent(body1, body2); |
| } |
| |
| private static boolean forClausesAreEquivalent(@Nullable GrForClause statement1, |
| @Nullable GrForClause statement2) { |
| if (statement1 == null && statement2 == null) return true; |
| if (statement1 == null || statement2 == null) return false; |
| final GrVariable var1 = statement1.getDeclaredVariable(); |
| final GrVariable var2 = statement2.getDeclaredVariable(); |
| if (var1 == null && var2 == null) return true; |
| if (var1 == null || var2 == null) return false; |
| return variablesAreEquivalent(var1, var2); |
| } |
| |
| private static boolean switchStatementsAreEquivalent(@NotNull GrSwitchStatement statement1, |
| @NotNull GrSwitchStatement statement2) { |
| final GrExpression switchExpression1 = statement1.getCondition(); |
| final GrExpression switchExpression2 = statement2.getCondition(); |
| if (!expressionsAreEquivalent(switchExpression1, switchExpression2)) { |
| return false; |
| } |
| final GrCaseSection[] clauses1 = statement1.getCaseSections(); |
| final GrCaseSection[] clauses2 = statement2.getCaseSections(); |
| if (clauses1.length != clauses2.length) { |
| return false; |
| } |
| for (int i = 0; i < clauses1.length; i++) { |
| final GrCaseSection clause1 = clauses1[i]; |
| final GrCaseSection clause2 = clauses2[i]; |
| if (!caseClausesAreEquivalent(clause1, clause2)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean caseClausesAreEquivalent(GrCaseSection clause1, GrCaseSection clause2) { |
| final GrCaseLabel[] label1 = clause1.getCaseLabels(); |
| final GrCaseLabel[] label2 = clause2.getCaseLabels(); |
| if (label1.length != label2.length) return false; |
| |
| for (int i = 0; i < label1.length; i++) { |
| GrCaseLabel l1 = label1[i]; |
| GrCaseLabel l2 = label2[i]; |
| |
| if (!expressionsAreEquivalent(l1.getValue(), l2.getValue())) { |
| return false; |
| } |
| } |
| |
| final GrStatement[] statements1 = clause1.getStatements(); |
| final GrStatement[] statements2 = clause2.getStatements(); |
| if (statements1.length != statements2.length) { |
| return false; |
| } |
| for (int i = 0; i < statements1.length; i++) { |
| if (!statementsAreEquivalent(statements1[i], statements2[i])) { |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean blockStatementsAreEquivalent(@NotNull GrBlockStatement statement1, |
| @NotNull GrBlockStatement statement2) { |
| final GrOpenBlock block1 = statement1.getBlock(); |
| final GrOpenBlock block2 = statement2.getBlock(); |
| return openBlocksAreEquivalent(block1, block2); |
| } |
| |
| private static boolean openBlocksAreEquivalent(@Nullable GrOpenBlock block1, @Nullable GrOpenBlock block2) { |
| if (block1 == null || block2 == null) return false; |
| |
| final GrStatement[] statements1 = block1.getStatements(); |
| final GrStatement[] statements2 = block2.getStatements(); |
| if (statements1.length != statements2.length) { |
| return false; |
| } |
| for (int i = 0; i < statements1.length; i++) { |
| if (!statementsAreEquivalent(statements1[i], statements2[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean ifStatementsAreEquivalent(@NotNull GrIfStatement statement1, |
| @NotNull GrIfStatement statement2) { |
| final GrExpression condition1 = statement1.getCondition(); |
| final GrExpression condition2 = statement2.getCondition(); |
| final GrStatement thenBranch1 = statement1.getThenBranch(); |
| final GrStatement thenBranch2 = statement2.getThenBranch(); |
| final GrStatement elseBranch1 = statement1.getElseBranch(); |
| final GrStatement elseBranch2 = statement2.getElseBranch(); |
| return expressionsAreEquivalent(condition1, condition2) && |
| statementsAreEquivalent(thenBranch1, thenBranch2) && |
| statementsAreEquivalent(elseBranch1, elseBranch2); |
| } |
| |
| private static boolean expressionStatementsAreEquivalent(@NotNull GrExpression statement1, |
| @NotNull GrExpression statement2) { |
| return expressionsAreEquivalent(statement1, statement2); |
| } |
| |
| private static boolean returnStatementsAreEquivalent(@NotNull GrReturnStatement statement1, |
| @NotNull GrReturnStatement statement2) { |
| final GrExpression returnValue1 = statement1.getReturnValue(); |
| final GrExpression returnValue2 = statement2.getReturnValue(); |
| return expressionsAreEquivalent(returnValue1, returnValue2); |
| } |
| |
| private static boolean throwStatementsAreEquivalent(@NotNull GrThrowStatement statement1, |
| @NotNull GrThrowStatement statement2) { |
| final GrExpression exception1 = statement1.getException(); |
| final GrExpression exception2 = statement2.getException(); |
| return expressionsAreEquivalent(exception1, exception2); |
| } |
| |
| @SuppressWarnings({"ConstantConditions"}) |
| public static boolean expressionsAreEquivalent(@Nullable GrExpression exp1, |
| @Nullable GrExpression exp2) { |
| if (exp1 == null && exp2 == null) { |
| return true; |
| } |
| if (exp1 == null || exp2 == null) { |
| return false; |
| } |
| GrExpression expToCompare1 = (GrExpression)PsiUtil.skipParentheses(exp1, false); |
| GrExpression expToCompare2 = (GrExpression)PsiUtil.skipParentheses(exp2, false); |
| final int type1 = getExpressionType(expToCompare1); |
| final int type2 = getExpressionType(expToCompare2); |
| if (type1 != type2) { |
| return false; |
| } |
| switch (type1) { |
| case LITERAL_EXPRESSION: |
| case REFERENCE_EXPRESSION: |
| final String text1 = expToCompare1.getText(); |
| final String text2 = expToCompare2.getText(); |
| return text1.equals(text2); |
| case CALL_EXPRESSION: |
| return methodCallExpressionsAreEquivalent((GrMethodCall) expToCompare1, |
| (GrMethodCall) expToCompare2); |
| case NEW_EXPRESSION: |
| return newExpressionsAreEquivalent((GrNewExpression) expToCompare1, |
| (GrNewExpression) expToCompare2); |
| case ARRAY_LITERAL_EXPRESSION: |
| return arrayDeclarationsAreEquivalent((GrArrayDeclaration) expToCompare1, |
| (GrArrayDeclaration) expToCompare2); |
| case PREFIX_EXPRESSION: |
| return prefixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, |
| (GrUnaryExpression) expToCompare2); |
| case POSTFIX_EXPRESSION: |
| return postfixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, |
| (GrUnaryExpression) expToCompare2); |
| case BINARY_EXPRESSION: |
| return binaryExpressionsAreEquivalent((GrBinaryExpression) expToCompare1, |
| (GrBinaryExpression) expToCompare2); |
| case ASSIGNMENT_EXPRESSION: |
| return assignmentExpressionsAreEquivalent((GrAssignmentExpression) expToCompare1, |
| (GrAssignmentExpression) expToCompare2); |
| case CONDITIONAL_EXPRESSION: |
| return conditionalExpressionsAreEquivalent((GrConditionalExpression) expToCompare1, |
| (GrConditionalExpression) expToCompare2); |
| case ELVIS_EXPRESSION: |
| return elvisExpressionsAreEquivalent((GrElvisExpression) expToCompare1, |
| (GrElvisExpression) expToCompare2); |
| case RANGE_EXPRESSION: |
| return rangeExpressionsAreEquivalent((GrRangeExpression) expToCompare1, |
| (GrRangeExpression) expToCompare2); |
| case TYPE_CAST_EXPRESSION: |
| return typecastExpressionsAreEquivalent((GrTypeCastExpression) expToCompare1, |
| (GrTypeCastExpression) expToCompare2); |
| case SAFE_CAST_EXPRESSION: |
| return safeCastExpressionsAreEquivalent((GrSafeCastExpression)expToCompare1, |
| (GrSafeCastExpression)expToCompare2); |
| case INSTANCEOF_EXPRESSION: |
| return instanceofExpressionsAreEquivalent((GrInstanceOfExpression) expToCompare1, |
| (GrInstanceOfExpression) expToCompare2); |
| case INDEX_EXPRESSION: |
| return indexExpressionsAreEquivalent((GrIndexProperty) expToCompare1, |
| (GrIndexProperty) expToCompare2); |
| case LIST_OR_MAP_EXPRESSION: |
| return listOrMapExpressionsAreEquivalent((GrListOrMap) expToCompare1, |
| (GrListOrMap) expToCompare2); |
| case CLOSABLE_BLOCK_EXPRESSION: |
| return closableBlockExpressionsAreEquivalent((GrClosableBlock) expToCompare1, |
| (GrClosableBlock) expToCompare2); |
| case PROPERTY_SELECTION_EXPRESSION: |
| return textOfExpressionsIsEquivalent(expToCompare1, expToCompare2); // todo |
| default: |
| return false; |
| } |
| } |
| |
| private static boolean textOfExpressionsIsEquivalent(GrExpression expToCompare1, |
| GrExpression expToCompare2) { |
| final String text1 = expToCompare1.getText(); |
| final String text2 = expToCompare2.getText(); |
| return text1.equals(text2); |
| } |
| |
| private static boolean closableBlockExpressionsAreEquivalent(GrClosableBlock closableBlock1, |
| GrClosableBlock closableBlock2) { |
| final GrStatement[] statements1 = closableBlock1.getStatements(); |
| final GrStatement[] statements2 = closableBlock2.getStatements(); |
| if (statements1.length != statements2.length) { |
| return false; |
| } |
| for (int i = 0; i < statements1.length; i++) { |
| if (!statementsAreEquivalent(statements1[i], statements2[i])) { |
| return false; |
| } |
| } |
| |
| GrParameter[] parameters1 = closableBlock1.getParameters(); |
| GrParameter[] parameters2 = closableBlock2.getParameters(); |
| return parametersAreEquivalent(parameters1, parameters2); |
| } |
| |
| private static boolean parametersAreEquivalent(GrParameter[] parameters1, GrParameter[] parameters2) { |
| if (parameters1.length != parameters2.length) return false; |
| for (int i = 0; i < parameters1.length; i++) { |
| if (!parametersAreEquivalent(parameters1[i], parameters2[i])) return false; |
| } |
| return true; |
| } |
| |
| private static boolean listOrMapExpressionsAreEquivalent(GrListOrMap expression1, GrListOrMap expression2) { |
| return expressionListsAreEquivalent(expression1.getInitializers(), expression2.getInitializers()) && |
| namedArgumentListsAreEquivalent(expression1.getNamedArguments(), expression2.getNamedArguments()); |
| } |
| |
| private static boolean arrayDeclarationsAreEquivalent(GrArrayDeclaration expression1, |
| GrArrayDeclaration expression2) { |
| final int count1 = expression1.getArrayCount(); |
| final int count2 = expression2.getArrayCount(); |
| if (count1 != count2) { |
| return false; |
| } |
| final GrExpression[] bounds1 = expression1.getBoundExpressions(); |
| final GrExpression[] bounds2 = expression2.getBoundExpressions(); |
| return expressionListsAreEquivalent(bounds1, bounds2); |
| } |
| |
| private static boolean instanceofExpressionsAreEquivalent(GrInstanceOfExpression expression1, |
| GrInstanceOfExpression expression2) { |
| final GrExpression operand1 = expression1.getOperand(); |
| final GrExpression operand2 = expression2.getOperand(); |
| if (!expressionsAreEquivalent(operand1, operand2)) { |
| return false; |
| } |
| GrTypeElement typeElement1 = expression1.getTypeElement(); |
| GrTypeElement typeElement2 = expression2.getTypeElement(); |
| if (typeElement1 == null || typeElement2 == null) return false; |
| |
| final PsiType type1 = typeElement1.getType(); |
| final PsiType type2 = typeElement2.getType(); |
| return typesAreEquivalent(type1, type2); |
| } |
| |
| private static boolean indexExpressionsAreEquivalent(GrIndexProperty expression1, |
| GrIndexProperty expression2) { |
| return expressionsAreEquivalent(expression1.getInvokedExpression(), expression2.getInvokedExpression()) && |
| argumentListsAreEquivalent(expression1.getArgumentList(), expression2.getArgumentList()); |
| } |
| |
| private static boolean typecastExpressionsAreEquivalent(GrTypeCastExpression expression1, |
| GrTypeCastExpression expression2) { |
| final GrExpression operand1 = expression1.getOperand(); |
| final GrExpression operand2 = expression2.getOperand(); |
| if (!expressionsAreEquivalent(operand1, operand2)) { |
| return false; |
| } |
| final PsiType type1 = expression1.getCastTypeElement().getType(); |
| final PsiType type2 = expression2.getCastTypeElement().getType(); |
| return typesAreEquivalent(type1, type2); |
| } |
| |
| private static boolean safeCastExpressionsAreEquivalent(GrSafeCastExpression expression1, |
| GrSafeCastExpression expression2) { |
| final GrExpression operand1 = expression1.getOperand(); |
| final GrExpression operand2 = expression2.getOperand(); |
| if (!expressionsAreEquivalent(operand1, operand2)) { |
| return false; |
| } |
| final GrTypeElement typeElement1 = expression1.getCastTypeElement(); |
| final GrTypeElement typeElement2 = expression2.getCastTypeElement(); |
| final PsiType safe1 = typeElement1 == null ? null : typeElement1.getType(); |
| final PsiType safe2 = typeElement2 == null ? null : typeElement2.getType(); |
| return typesAreEquivalent(safe1, safe2); |
| } |
| |
| private static boolean methodCallExpressionsAreEquivalent(@NotNull GrMethodCall methodExp1, |
| @NotNull GrMethodCall methodExp2) { |
| final GrExpression methodExpression1 = methodExp1.getInvokedExpression(); |
| final GrExpression methodExpression2 = methodExp2.getInvokedExpression(); |
| if (!expressionsAreEquivalent(methodExpression1, methodExpression2)) { |
| return false; |
| } |
| final GrClosableBlock[] closures1 = methodExp1.getClosureArguments(); |
| final GrClosableBlock[] closures2 = methodExp2.getClosureArguments(); |
| if (!expressionListsAreEquivalent(closures1, closures2)) { |
| return false; |
| } |
| |
| return argumentListsAreEquivalent(methodExp1.getArgumentList(), methodExp2.getArgumentList()); |
| } |
| |
| private static boolean argumentListsAreEquivalent(@Nullable GrArgumentList list1, @Nullable GrArgumentList list2) { |
| if (list1 == null && list2 == null) { |
| return true; |
| } |
| if (list1 == null || list2 == null) { |
| return false; |
| } |
| final GrExpression[] args1 = list1.getExpressionArguments(); |
| final GrExpression[] args2 = list2.getExpressionArguments(); |
| if (!expressionListsAreEquivalent(args1, args2)) { |
| return false; |
| } |
| final GrNamedArgument[] namedArgs1 = list1.getNamedArguments(); |
| final GrNamedArgument[] namedArgs2 = list2.getNamedArguments(); |
| if (!namedArgumentListsAreEquivalent(namedArgs1, namedArgs2)) { |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean namedArgumentListsAreEquivalent(GrNamedArgument[] namedArgs1, GrNamedArgument[] namedArgs2) { |
| if (namedArgs1.length != namedArgs2.length) { |
| return false; |
| } |
| for (GrNamedArgument arg1 : namedArgs1) { |
| final GrArgumentLabel label1 = arg1.getLabel(); |
| if (label1 == null) { |
| return false; |
| } |
| final String name1 = label1.getName(); |
| boolean found = false; |
| final GrExpression expression1 = arg1.getExpression(); |
| for (GrNamedArgument arg2 : namedArgs2) { |
| final GrArgumentLabel label2 = arg2.getLabel(); |
| if (label2 == null) { |
| return false; |
| } |
| final String name2 = label2.getName(); |
| final GrExpression expression2 = arg2.getExpression(); |
| if (name1 == null) { |
| if (name2 == null && |
| expressionsAreEquivalent(((GrExpression)label1.getNameElement()), (GrExpression)label2.getNameElement()) && |
| expressionsAreEquivalent(expression1, expression2)) { |
| found = true; |
| break; |
| } |
| } |
| else if (name1.equals(name2) && expressionsAreEquivalent(expression1, expression2)) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean newExpressionsAreEquivalent(@NotNull GrNewExpression newExp1, |
| @NotNull GrNewExpression newExp2) { |
| final PsiMethod constructor1 = newExp1.resolveMethod(); |
| final PsiMethod constructor2 = newExp2.resolveMethod(); |
| if (constructor1 == null || constructor2 == null || constructor1.equals(constructor2)) { |
| return false; |
| } |
| return argumentListsAreEquivalent(newExp1.getArgumentList(), newExp2.getArgumentList()); |
| } |
| |
| private static boolean prefixExpressionsAreEquivalent(@NotNull GrUnaryExpression prefixExp1, |
| @NotNull GrUnaryExpression prefixExp2) { |
| final IElementType sign1 = prefixExp1.getOperationTokenType(); |
| final IElementType sign2 = prefixExp2.getOperationTokenType(); |
| if (sign1 != sign2) { |
| return false; |
| } |
| final GrExpression operand1 = prefixExp1.getOperand(); |
| final GrExpression operand2 = prefixExp2.getOperand(); |
| return expressionsAreEquivalent(operand1, operand2); |
| } |
| |
| private static boolean postfixExpressionsAreEquivalent(@NotNull GrUnaryExpression postfixExp1, |
| @NotNull GrUnaryExpression postfixExp2) { |
| final IElementType sign1 = postfixExp1.getOperationTokenType(); |
| final IElementType sign2 = postfixExp2.getOperationTokenType(); |
| if (sign1 != sign2) { |
| return false; |
| } |
| final GrExpression operand1 = postfixExp1.getOperand(); |
| final GrExpression operand2 = postfixExp2.getOperand(); |
| return expressionsAreEquivalent(operand1, operand2); |
| } |
| |
| private static boolean binaryExpressionsAreEquivalent(@NotNull GrBinaryExpression binaryExp1, |
| @NotNull GrBinaryExpression binaryExp2) { |
| final IElementType sign1 = binaryExp1.getOperationTokenType(); |
| final IElementType sign2 = binaryExp2.getOperationTokenType(); |
| if (sign1 != sign2) { |
| return false; |
| } |
| final GrExpression lhs1 = binaryExp1.getLeftOperand(); |
| final GrExpression lhs2 = binaryExp2.getLeftOperand(); |
| final GrExpression rhs1 = binaryExp1.getRightOperand(); |
| final GrExpression rhs2 = binaryExp2.getRightOperand(); |
| return expressionsAreEquivalent(lhs1, lhs2) |
| && expressionsAreEquivalent(rhs1, rhs2); |
| } |
| |
| private static boolean rangeExpressionsAreEquivalent(@NotNull GrRangeExpression rangeExp1, |
| @NotNull GrRangeExpression rangeExp2) { |
| return expressionsAreEquivalent(rangeExp1.getLeftOperand(), rangeExp2.getLeftOperand()) && |
| expressionsAreEquivalent(rangeExp1.getRightOperand(), rangeExp2.getRightOperand()) && |
| isInclusive(rangeExp1) == isInclusive(rangeExp2); |
| } |
| |
| private static boolean isInclusive(GrRangeExpression range) { |
| for (PsiElement child : range.getChildren()) { |
| if ("..".equals(child.getText())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean assignmentExpressionsAreEquivalent(@NotNull GrAssignmentExpression assignExp1, |
| @NotNull GrAssignmentExpression assignExp2) { |
| final IElementType sign1 = assignExp1.getOperationTokenType(); |
| final IElementType sign2 = assignExp2.getOperationTokenType(); |
| if (sign1 != sign2) { |
| return false; |
| } |
| final GrExpression lhs1 = assignExp1.getLValue(); |
| final GrExpression lhs2 = assignExp2.getLValue(); |
| final GrExpression rhs1 = assignExp1.getRValue(); |
| final GrExpression rhs2 = assignExp2.getRValue(); |
| return expressionsAreEquivalent(lhs1, lhs2) |
| && expressionsAreEquivalent(rhs1, rhs2); |
| } |
| |
| private static boolean conditionalExpressionsAreEquivalent(@NotNull GrConditionalExpression condExp1, |
| @NotNull GrConditionalExpression condExp2) { |
| final GrExpression condition1 = condExp1.getCondition(); |
| final GrExpression condition2 = condExp2.getCondition(); |
| final GrExpression thenExpression1 = condExp1.getThenBranch(); |
| final GrExpression thenExpression2 = condExp2.getThenBranch(); |
| final GrExpression elseExpression1 = condExp1.getElseBranch(); |
| final GrExpression elseExpression2 = condExp2.getElseBranch(); |
| return expressionsAreEquivalent(condition1, condition2) && |
| expressionsAreEquivalent(thenExpression1, thenExpression2) && |
| expressionsAreEquivalent(elseExpression1, elseExpression2); |
| } |
| |
| private static boolean elvisExpressionsAreEquivalent(@NotNull GrElvisExpression condExp1, |
| @NotNull GrElvisExpression condExp2) { |
| final GrExpression condition1 = condExp1.getCondition(); |
| final GrExpression condition2 = condExp2.getCondition(); |
| final GrExpression elseExpression1 = condExp1.getElseBranch(); |
| final GrExpression elseExpression2 = condExp2.getElseBranch(); |
| return expressionsAreEquivalent(condition1, condition2) |
| && expressionsAreEquivalent(elseExpression1, elseExpression2); |
| } |
| |
| private static boolean expressionListsAreEquivalent(@Nullable GrExpression[] expressions1, |
| @Nullable GrExpression[] expressions2) { |
| if (expressions1 == null && expressions2 == null) { |
| return true; |
| } |
| if (expressions1 == null || expressions2 == null) { |
| return false; |
| } |
| if (expressions1.length != expressions2.length) { |
| return false; |
| } |
| for (int i = 0; i < expressions1.length; i++) { |
| if (!expressionsAreEquivalent(expressions1[i], expressions2[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static int getExpressionType(@Nullable GrExpression exp) { |
| if (exp instanceof GrArrayDeclaration) { |
| return ARRAY_LITERAL_EXPRESSION; |
| } |
| if (exp instanceof GrLiteral) { |
| return LITERAL_EXPRESSION; |
| } |
| if (exp instanceof GrReferenceExpression) { |
| return REFERENCE_EXPRESSION; |
| } |
| if (exp instanceof GrTypeCastExpression) { |
| return TYPE_CAST_EXPRESSION; |
| } |
| if (exp instanceof GrSafeCastExpression) { |
| return SAFE_CAST_EXPRESSION; |
| } |
| if (exp instanceof GrInstanceOfExpression) { |
| return INSTANCEOF_EXPRESSION; |
| } |
| if (exp instanceof GrNewExpression) { |
| return NEW_EXPRESSION; |
| } |
| if (exp instanceof GrMethodCall) { |
| return CALL_EXPRESSION; |
| } |
| if (exp instanceof GrUnaryExpression) { |
| return ((GrUnaryExpression)exp).isPostfix() ? POSTFIX_EXPRESSION : PREFIX_EXPRESSION; |
| } |
| if (exp instanceof GrAssignmentExpression) { |
| return ASSIGNMENT_EXPRESSION; |
| } |
| if (exp instanceof GrRangeExpression) { |
| return RANGE_EXPRESSION; |
| } |
| if (exp instanceof GrBinaryExpression) { |
| return BINARY_EXPRESSION; |
| } |
| if (exp instanceof GrElvisExpression) { |
| return ELVIS_EXPRESSION; |
| } |
| if (exp instanceof GrConditionalExpression) { |
| return CONDITIONAL_EXPRESSION; |
| } |
| if (exp instanceof GrIndexProperty) { |
| return INDEX_EXPRESSION; |
| } |
| if (exp instanceof GrListOrMap) { |
| return LIST_OR_MAP_EXPRESSION; |
| } |
| if (exp instanceof GrClosableBlock) { |
| return CLOSABLE_BLOCK_EXPRESSION; |
| } |
| return -1; // Type of expression can be defined in third party plugins. See issue #IDEA-59846 |
| } |
| |
| private static int getStatementType(@Nullable GrStatement statement) { |
| if (statement instanceof GrBlockStatement) { |
| return BLOCK_STATEMENT; |
| } |
| if (statement instanceof GrBreakStatement) { |
| return BREAK_STATEMENT; |
| } |
| if (statement instanceof GrContinueStatement) { |
| return CONTINUE_STATEMENT; |
| } |
| if (statement instanceof GrVariableDeclaration) { |
| return VAR_STATEMENT; |
| } |
| if (statement instanceof GrApplicationStatement) { |
| return APPLICATION_STATEMENT; |
| } |
| if (statement instanceof GrExpression) { |
| return EXPRESSION_STATEMENT; |
| } |
| if (statement instanceof GrForStatement) { |
| return FOR_STATEMENT; |
| } |
| if (statement instanceof GrIfStatement) { |
| return IF_STATEMENT; |
| } |
| if (statement instanceof GrReturnStatement) { |
| return RETURN_STATEMENT; |
| } |
| if (statement instanceof GrSwitchStatement) { |
| return SWITCH_STATEMENT; |
| } |
| if (statement instanceof GrThrowStatement) { |
| return THROW_STATEMENT; |
| } |
| if (statement instanceof GrTryCatchStatement) { |
| return TRY_STATEMENT; |
| } |
| if (statement instanceof GrWhileStatement) { |
| return WHILE_STATEMENT; |
| } |
| if (statement instanceof GrSynchronizedStatement) { |
| return SYNCHRONIZED_STATEMENT; |
| } |
| if (statement instanceof GrAssertStatement) { |
| return ASSERT_STATEMENT; |
| } |
| |
| return -1; // Type of expression can be defined in third party plugins. See issue #IDEA-59846 |
| } |
| } |