private static boolean textOfExpressionsIsEquivalent(
     GrExpression expToCompare1, GrExpression expToCompare2) {
   final String text1 = expToCompare1.getText();
   final String text2 = expToCompare2.getText();
   return text1.equals(text2);
 }
  @Nullable
  public static GrExpression replaceExpression(
      GrExpression oldExpr, GrExpression newExpr, boolean removeUnnecessaryParentheses) {
    PsiElement oldParent = oldExpr.getParent();
    if (oldParent == null) throw new PsiInvalidElementAccessException(oldExpr);

    if (!(oldExpr instanceof GrApplicationStatement)) {
      newExpr = ApplicationStatementUtil.convertToMethodCallExpression(newExpr);
    }

    // Remove unnecessary parentheses
    if (removeUnnecessaryParentheses
        && oldParent instanceof GrParenthesizedExpression
        && !(oldParent.getParent() instanceof GrArgumentLabel)) {
      return ((GrExpression) oldParent)
          .replaceWithExpression(newExpr, removeUnnecessaryParentheses);
    }

    // regexes cannot be after identifier , try to replace it with simple string
    if (getRegexAtTheBeginning(newExpr) != null && isAfterIdentifier(oldExpr)) {
      final PsiElement copy = newExpr.copy();
      final GrLiteral regex = getRegexAtTheBeginning(copy);
      LOG.assertTrue(regex != null);
      final GrLiteral stringLiteral = GrStringUtil.createStringFromRegex(regex);
      if (regex == copy) {
        return oldExpr.replaceWithExpression(stringLiteral, removeUnnecessaryParentheses);
      } else {
        regex.replace(stringLiteral);
        return oldExpr.replaceWithExpression((GrExpression) copy, removeUnnecessaryParentheses);
      }
    }

    GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
    if (oldParent instanceof GrStringInjection) {
      if (newExpr instanceof GrString
          || newExpr instanceof GrLiteral && ((GrLiteral) newExpr).getValue() instanceof String) {
        return GrStringUtil.replaceStringInjectionByLiteral(
            (GrStringInjection) oldParent, (GrLiteral) newExpr);
      } else {
        newExpr = factory.createExpressionFromText("{" + newExpr.getText() + "}");
        oldParent.getNode().replaceChild(oldExpr.getNode(), newExpr.getNode());
        return newExpr;
      }
    }

    if (PsiTreeUtil.getParentOfType(oldExpr, GrStringInjection.class, false, GrCodeBlock.class)
        != null) {
      final PsiElement replaced = oldExpr.replace(newExpr);
      final GrStringInjection stringInjection =
          PsiTreeUtil.getParentOfType(replaced, GrStringInjection.class);
      GrStringUtil.wrapInjection(stringInjection);
      assert stringInjection != null;
      return stringInjection.getClosableBlock();
    }

    // check priorities
    if (oldParent instanceof GrExpression && !(oldParent instanceof GrParenthesizedExpression)) {
      GrExpression addedParenth =
          addParenthesesIfNeeded(newExpr, oldExpr, (GrExpression) oldParent);
      if (newExpr != addedParenth) {
        return oldExpr.replaceWithExpression(addedParenth, removeUnnecessaryParentheses);
      }
    }

    // if replace closure argument with expression
    // we should add the expression in arg list
    if (oldExpr instanceof GrClosableBlock
        && !(newExpr instanceof GrClosableBlock)
        && oldParent instanceof GrMethodCallExpression
        && ArrayUtil.contains(
            oldExpr, ((GrMethodCallExpression) oldParent).getClosureArguments())) {
      return ((GrMethodCallExpression) oldParent)
          .replaceClosureArgument((GrClosableBlock) oldExpr, newExpr);
    }

    newExpr = (GrExpression) oldExpr.replace(newExpr);

    // if newExpr is the first grand child of command argument list we should replace command arg
    // list with parenthesised arg list.
    // In other case the code will be broken. So we try to find wrapping command arg list counting
    // levels. After arg list replace we go inside it
    // to find target parenthesised expression.
    if (newExpr instanceof GrParenthesizedExpression && isFirstChild(newExpr)) {
      int parentCount = 0;

      PsiElement element = oldParent;
      while (element != null && !(element instanceof GrCommandArgumentList)) {
        if (element instanceof GrCodeBlock || element instanceof GrParenthesizedExpression) break;
        if (element instanceof PsiFile) break;

        final PsiElement parent = element.getParent();
        if (parent == null) break;
        if (!isFirstChild(element)) break;

        element = parent;
        parentCount++;
      }

      if (element instanceof GrCommandArgumentList) {
        final GrCommandArgumentList commandArgList = (GrCommandArgumentList) element;

        final PsiElement parent = commandArgList.getParent();
        LOG.assertTrue(parent instanceof GrApplicationStatement);

        final GrMethodCall methodCall =
            factory.createMethodCallByAppCall((GrApplicationStatement) parent);
        final GrMethodCall newCall = (GrMethodCall) parent.replace(methodCall);

        PsiElement result = newCall.getArgumentList().getAllArguments()[0];

        for (int i = 0; i < parentCount; i++) {
          result = PsiUtil.skipWhitespacesAndComments(result.getFirstChild(), true);
        }

        LOG.assertTrue(result instanceof GrParenthesizedExpression);
        return (GrExpression) result;
      }
    }
    return newExpr;
  }
 @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 THIS_EXPRESSION:
     case SUPER_EXPRESSION:
       return true;
     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;
   }
 }