/*
  To declare or not a variable to which method call result will be assigned.
   */
  private static List<VariableInfo> mustAddVariableDeclaration(
      @NotNull GrStatement[] statements, @NotNull VariableInfo[] vars) {
    Map<String, VariableInfo> names = new HashMap<String, VariableInfo>();
    for (VariableInfo var : vars) {
      names.put(var.getName(), var);
    }
    List<VariableInfo> result = new ArrayList<VariableInfo>();

    for (GrStatement statement : statements) {
      if (statement instanceof GrVariableDeclaration) {
        GrVariableDeclaration declaration = (GrVariableDeclaration) statement;
        for (GrVariable variable : declaration.getVariables()) {
          final VariableInfo removed = names.remove(variable.getName());
          if (removed != null) {
            result.add(removed);
          }
        }
      }
    }
    for (String varName : names.keySet()) {
      if (ResolveUtil.resolveProperty(statements[0], varName) == null) {
        result.add(names.get(varName));
      }
    }

    return result;
  }
  public static void removeVariable(GrVariable variable) {
    final GrVariableDeclaration varDecl = (GrVariableDeclaration) variable.getParent();
    final List<GrVariable> variables = Arrays.asList(varDecl.getVariables());
    if (!variables.contains(variable)) {
      throw new IllegalArgumentException();
    }

    final PsiElement parent = varDecl.getParent();
    final ASTNode owner = parent.getNode();
    if (variables.size() == 1 && owner != null) {
      PsiElement next = varDecl.getNextSibling();

      // remove redundant semicolons
      //noinspection ConstantConditions
      while (next != null && next.getNode() != null && next.getNode().getElementType() == mSEMI) {
        PsiElement tmpNext = next.getNextSibling();
        //noinspection ConstantConditions
        next.delete();
        next = tmpNext;
      }

      removeNewLineAfter(varDecl);
      varDecl.delete();
      return;
    }
    variable.delete();
  }
  public GrInplaceConstantIntroducer(
      GrIntroduceContext context, OccurrencesChooser.ReplaceChoice choice) {
    super(IntroduceConstantHandler.REFACTORING_NAME, choice, context);

    myContext = context;

    myPanel = new GrInplaceIntroduceConstantPanel();

    GrVariable localVar = GrIntroduceHandlerBase.resolveLocalVar(context);
    if (localVar != null) {
      ArrayList<String> result = ContainerUtil.newArrayList(localVar.getName());

      GrExpression initializer = localVar.getInitializerGroovy();
      if (initializer != null) {
        ContainerUtil.addAll(
            result,
            GroovyNameSuggestionUtil.suggestVariableNames(
                initializer, new GroovyInplaceFieldValidator(context), true));
      }
      mySuggestedNames = ArrayUtil.toStringArray(result);
    } else {
      GrExpression expression = context.getExpression();
      assert expression != null;
      mySuggestedNames =
          GroovyNameSuggestionUtil.suggestVariableNames(
              expression, new GroovyInplaceFieldValidator(context), true);
    }
  }
  @Override
  protected void processIntention(@NotNull PsiElement element, Project project, Editor editor)
      throws IncorrectOperationException {
    final GrMethodCallExpression expression = (GrMethodCallExpression) element;
    final GrClosableBlock block = expression.getClosureArguments()[0];
    final GrParameterList parameterList = block.getParameterList();
    final GrParameter[] parameters = parameterList.getParameters();

    String var;
    if (parameters.length == 1) {
      var = parameters[0].getText();
      var = StringUtil.replace(var, GrModifier.DEF, "");
    } else {
      var = "it";
    }

    final GrExpression invokedExpression = expression.getInvokedExpression();
    GrExpression qualifier = ((GrReferenceExpression) invokedExpression).getQualifierExpression();
    final GroovyPsiElementFactory elementFactory =
        GroovyPsiElementFactory.getInstance(element.getProject());
    if (qualifier == null) {
      qualifier = elementFactory.createExpressionFromText("this");
    }

    StringBuilder builder = new StringBuilder();
    builder.append("for (").append(var).append(" in ").append(qualifier.getText()).append(") {\n");
    String text = block.getText();
    final PsiElement blockArrow = block.getArrow();
    int index;
    if (blockArrow != null) {
      index = blockArrow.getStartOffsetInParent() + blockArrow.getTextLength();
    } else {
      index = 1;
    }
    while (index < text.length() && Character.isWhitespace(text.charAt(index))) index++;
    text = text.substring(index, text.length() - 1);
    builder.append(text);
    builder.append("}");

    final GrStatement statement = elementFactory.createStatementFromText(builder.toString());
    GrForStatement forStatement = (GrForStatement) expression.replaceWithStatement(statement);
    final GrForClause clause = forStatement.getClause();
    GrVariable variable = clause.getDeclaredVariable();

    forStatement = updateReturnStatements(forStatement);

    if (variable == null) return;

    if (ApplicationManager.getApplication().isUnitTestMode()) return;

    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document doc = documentManager.getDocument(element.getContainingFile());
    if (doc == null) return;

    documentManager.doPostponedOperationsAndUnblockDocument(doc);
    editor.getCaretModel().moveToOffset(variable.getTextOffset());
    new VariableInplaceRenamer(variable, editor).performInplaceRename();
  }
 @Override
 protected void checkVariable(@NotNull GrVariable variable) throws GrRefactoringError {
   final GrExpression initializer = variable.getInitializerGroovy();
   if (initializer == null) {
     throw new GrRefactoringError(
         RefactoringBundle.message("variable.does.not.have.an.initializer", variable.getName()));
   }
   checkExpression(initializer);
 }
 private void flushForeachLoopVariable(@Nullable GrForClause clause) {
   if (clause instanceof GrForInClause) {
     GrVariable variable = clause.getDeclaredVariable();
     if (variable != null && myPolicy.isVariableInitialized(variable)) {
       addNodeAndCheckPending(
           new ReadWriteVariableInstruction(variable.getName(), variable, WRITE));
     }
   }
 }
  private static void appendInferredType(
      PsiElement originalElement, GrVariable variable, StringBuilder buffer) {
    PsiType inferredType = null;
    if (PsiImplUtil.isWhiteSpaceOrNls(originalElement)) {
      originalElement = PsiTreeUtil.prevLeaf(originalElement);
    }
    if (originalElement != null
        && originalElement.getNode().getElementType() == GroovyTokenTypes.mIDENT) {
      originalElement = originalElement.getParent();
    }
    if (originalElement instanceof GrReferenceExpression) {
      inferredType = ((GrReferenceExpression) originalElement).getType();
    } else if (originalElement instanceof GrVariableDeclaration) {
      inferredType = variable.getTypeGroovy();
    } else if (originalElement instanceof GrVariable) {
      inferredType = ((GrVariable) originalElement).getTypeGroovy();
    }

    if (inferredType != null) {
      buffer.append("[inferred type] ");
      appendTypeString(buffer, inferredType, originalElement);
    } else {
      buffer.append("[cannot infer type]");
    }
  }
  @Nullable
  private static String getTypeText(GrVariable var) {
    final PsiType type = var.getTypeGroovy();
    if (type == null) return null;

    return type.getCanonicalText();
  }
  public void visitVariable(GrVariable variable) {
    super.visitVariable(variable);

    if (myPolicy.isVariableInitialized(variable)) {
      ReadWriteVariableInstruction writeInst =
          new ReadWriteVariableInstruction(variable.getName(), variable, WRITE);
      addNodeAndCheckPending(writeInst);
    }
  }
  private NameSuggestionsField createNameField(GrVariable var) {
    List<String> names = new ArrayList<String>();
    if (var != null) {
      names.add(var.getName());
    }
    ContainerUtil.addAll(names, suggestNames());

    return new NameSuggestionsField(
        ArrayUtil.toStringArray(names), myProject, GroovyFileType.GROOVY_FILE_TYPE);
  }
 @Nullable
 private static PsiType getLeastUpperBoundByVar(final GrVariable resolved) {
   CachedValue<PsiType> data = resolved.getUserData(LEAST_UPPER_BOUND_TYPE);
   if (data == null) {
     data =
         CachedValuesManager.getManager(resolved.getProject())
             .createCachedValue(
                 new CachedValueProvider<PsiType>() {
                   @Override
                   public Result<PsiType> compute() {
                     return Result.create(
                         getLeastUpperBoundByVarImpl(resolved),
                         PsiModificationTracker.MODIFICATION_COUNT,
                         ProjectRootManager.getInstance(resolved.getProject()));
                   }
                 },
                 false);
   }
   return data.getValue();
 }
 @Override
 public GrTypeElement getTypeElementGroovyForVariable(GrVariable var) {
   if (isTuple()) {
     final PsiElement psiElement = PsiUtil.skipWhitespacesAndComments(var.getPrevSibling(), false);
     if (psiElement instanceof GrTypeElement) {
       return (GrTypeElement) psiElement;
     }
     return null;
   } else {
     return getTypeElementGroovy();
   }
 }
  private void alignVariableDeclarations(List<GrStatement> group, boolean classLevel) {
    AlignmentProvider.Aligner typeElement = myAlignmentProvider.createAligner(true);
    AlignmentProvider.Aligner varName = myAlignmentProvider.createAligner(true);
    AlignmentProvider.Aligner eq = myAlignmentProvider.createAligner(true);
    for (GrStatement statement : group) {
      GrVariableDeclaration varDeclaration = (GrVariableDeclaration) statement;
      GrVariable[] variables = varDeclaration.getVariables();
      for (GrVariable variable : variables) {
        varName.append(variable.getNameIdentifierGroovy());
      }

      if (classLevel && mySettings.ALIGN_GROUP_FIELD_DECLARATIONS) {
        typeElement.append(varDeclaration.getTypeElementGroovy());

        ASTNode current_eq =
            variables[variables.length - 1].getNode().findChildByType(GroovyTokenTypes.mASSIGN);
        if (current_eq != null) {
          eq.append(current_eq.getPsi());
        }
      }
    }
  }
  @NotNull
  public LinkedHashSet<String> suggestNames() {
    final GrExpression expression = myContext.getExpression();
    final GrVariable var = myContext.getVar();
    final StringPartInfo stringPart = myContext.getStringPart();

    if (expression != null) {
      return new LinkedHashSet<String>(
          Arrays.asList(
              GroovyNameSuggestionUtil.suggestVariableNames(expression, myValidator, myForStatic)));
    } else if (stringPart != null) {
      return new LinkedHashSet<String>(
          Arrays.asList(
              GroovyNameSuggestionUtil.suggestVariableNames(
                  stringPart.getLiteral(), myValidator, myForStatic)));
    } else {
      assert var != null;
      return new LinkedHashSet<String>(
          Arrays.asList(
              GroovyNameSuggestionUtil.suggestVariableNameByType(var.getType(), myValidator)));
    }
  }
  private GrTypeComboBox createTypeComboBox(
      GrVariable var, GrExpression expr, StringPartInfo stringPartInfo) {
    GrTypeComboBox box;
    if (var != null) {
      box = GrTypeComboBox.createTypeComboBoxWithDefType(var.getDeclaredType(), var);
    } else if (expr != null) {
      box = GrTypeComboBox.createTypeComboBoxFromExpression(expr);
    } else if (stringPartInfo != null) {
      box = GrTypeComboBox.createTypeComboBoxFromExpression(stringPartInfo.getLiteral());
    } else {
      box = GrTypeComboBox.createEmptyTypeComboBox();
    }

    box.addClosureTypesFrom(inferClosureReturnType(), myInfo.getContext());
    if (expr == null && var == null && stringPartInfo == null) {
      box.setSelectedIndex(box.getItemCount() - 1);
    }
    return box;
  }
 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();
   if (name1 == null) {
     return name2 == null;
   }
   return name1.equals(name2);
 }
  public static void generateBody(
      ExtractInfoHelper helper, boolean isVoid, StringBuilder buffer, boolean forceReturn) {
    VariableInfo[] outputInfos = helper.getOutputVariableInfos();

    ParameterInfo[] infos = helper.getParameterInfos();

    Set<String> declaredVars = new HashSet<String>();
    for (ParameterInfo info : infos) {
      declaredVars.add(info.getName());
    }

    for (VariableInfo info : mustAddVariableDeclaration(helper.getStatements(), outputInfos)) {
      declaredVars.add(info.getName());
    }

    List<VariableInfo> genDecl = new ArrayList<VariableInfo>();
    final Collection<GrVariable> outside =
        collectUsedLocalVarsOrParamsDeclaredOutside(helper.getStatements());

    for (final GrVariable variable : outside) {
      if (!declaredVars.contains(variable.getName())) {
        genDecl.add(
            new VariableInfo() {
              @NotNull
              @Override
              public String getName() {
                return variable.getName();
              }

              @Override
              public PsiType getType() {
                return variable.getDeclaredType();
              }
            });
      }
    }
    final List<GrStatement> statements =
        generateVarDeclarations(genDecl, helper.getProject(), null);
    for (GrStatement statement : statements) {
      buffer.append(statement.getText()).append('\n');
    }

    if (!isSingleExpression(helper.getStatements()) || statements.size() > 0) {
      for (PsiElement element : helper.getInnerElements()) {
        buffer.append(element.getText());
      }
      // append return statement
      if (!isVoid && outputInfos.length > 0) {
        buffer.append('\n');
        if (forceReturn) {
          buffer.append("return ");
        }
        if (outputInfos.length > 1) buffer.append('[');
        for (VariableInfo info : outputInfos) {
          buffer.append(info.getName()).append(", ");
        }
        buffer.delete(buffer.length() - 2, buffer.length());
        if (outputInfos.length > 1) buffer.append(']');
      }
    } else {
      GrExpression expr = (GrExpression) PsiUtil.skipParentheses(helper.getStatements()[0], false);
      boolean addReturn = !isVoid && forceReturn;
      if (addReturn) {
        buffer.append("return ");
        expr = ApplicationStatementUtil.convertToMethodCallExpression(expr);
        buffer.append(expr.getText());
      } else {
        buffer.append(expr != null ? expr.getText() : "");
      }
    }
  }
  private void calculateAlignments(List<ASTNode> children, boolean classLevel) {
    List<Alignment> currentGroup = null;
    for (ASTNode child : children) {
      PsiElement psi = child.getPsi();
      if (psi instanceof GrLabeledStatement) {
        List<LeafPsiElement> table = getSpockTable(((GrLabeledStatement) psi).getStatement());
        if (table.isEmpty()) {
          currentGroup = null;
        } else {
          currentGroup = new ArrayList<Alignment>();
          for (LeafPsiElement expression : table) {
            Alignment alignment = Alignment.createAlignment(true);
            currentGroup.add(alignment);
            ContainerUtil.putIfNotNull(expression, alignment, myInnerAlignments);
          }
        }
      } else if (currentGroup != null && isTablePart(psi)) {
        List<LeafPsiElement> table = getSpockTable((GrStatement) psi);
        for (int i = 0; i < Math.min(table.size(), currentGroup.size()); i++) {
          myInnerAlignments.put(table.get(i), currentGroup.get(i));
        }
      } else if (psi instanceof GrVariableDeclaration) {
        if (!classLevel || currentGroup == null || fieldGroupEnded(psi)) {
          currentGroup =
              Arrays.asList(
                  Alignment.createAlignment(true),
                  Alignment.createAlignment(true),
                  Alignment.createAlignment(true));
        }

        GrVariable[] variables = ((GrVariableDeclaration) psi).getVariables();
        if (variables.length > 0) {
          Alignment varName = currentGroup.get(1);
          for (GrVariable variable : variables) {
            myInnerAlignments.put(variable.getNameIdentifierGroovy(), varName);
          }

          if (classLevel && mySettings.ALIGN_GROUP_FIELD_DECLARATIONS) {
            ContainerUtil.putIfNotNull(
                ((GrVariableDeclaration) psi).getTypeElementGroovy(),
                currentGroup.get(0),
                myInnerAlignments);

            ASTNode eq =
                variables[variables.length - 1].getNode().findChildByType(GroovyTokenTypes.mASSIGN);
            if (eq != null) {
              myInnerAlignments.put(eq.getPsi(), currentGroup.get(2));
            }
          }
        }
      } else if (GeeseUtil.isClosureRBrace(psi) && myGroovySettings.USE_FLYING_GEESE_BRACES) {
        myInnerAlignments.put(psi, GeeseUtil.calculateRBraceAlignment(psi, myInnerAlignments));
      } else {
        if (psi instanceof PsiComment) {
          PsiElement prev = psi.getPrevSibling();
          if (prev != null && prev.getNode().getElementType() != mNLS
              || classLevel && !fieldGroupEnded(psi)) {
            continue;
          }
        }
        currentGroup = null;
      }
    }
  }
  @Override
  protected void performRefactoring(UsageInfo[] usages) {
    GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject);

    PsiType initializerType = mySettings.getSelectedType();

    // Changing external occurrences (the tricky part)

    IntroduceParameterUtil.processUsages(usages, this);

    final GrMethod toReplaceIn = (GrMethod) mySettings.getToReplaceIn();
    final PsiMethod toSearchFor = (PsiMethod) mySettings.getToSearchFor();

    final boolean methodsToProcessAreDifferent = toReplaceIn != toSearchFor;
    if (mySettings.generateDelegate()) {
      GroovyIntroduceParameterUtil.generateDelegate(toReplaceIn, myParameterInitializer, myProject);
      if (methodsToProcessAreDifferent) {
        final GrMethod method =
            GroovyIntroduceParameterUtil.generateDelegate(
                toSearchFor, myParameterInitializer, myProject);
        final PsiClass containingClass = method.getContainingClass();
        if (containingClass != null && containingClass.isInterface()) {
          final GrOpenBlock block = method.getBlock();
          if (block != null) {
            block.delete();
          }
        }
      }
    }

    // Changing signature of initial method
    // (signature of myMethodToReplaceIn will be either changed now or have already been changed)
    LOG.assertTrue(initializerType == null || initializerType.isValid());

    final FieldConflictsResolver fieldConflictsResolver =
        new FieldConflictsResolver(mySettings.getName(), toReplaceIn.getBlock());
    IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(
        new UsageInfo(toReplaceIn), usages, this);
    if (methodsToProcessAreDifferent) {
      IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(
          new UsageInfo(toSearchFor), usages, this);
    }

    // Replacing expression occurrences
    for (UsageInfo usage : usages) {
      if (usage instanceof ChangedMethodCallInfo) {
        PsiElement element = usage.getElement();

        GroovyIntroduceParameterUtil.processChangedMethodCall(element, mySettings, myProject);
      } else if (usage instanceof InternalUsageInfo) {
        PsiElement element = usage.getElement();
        if (element == null) continue;
        GrExpression newExpr = factory.createExpressionFromText(mySettings.getName());
        if (element instanceof GrExpression) {
          ((GrExpression) element).replaceWithExpression(newExpr, true);
        } else {
          element.replace(newExpr);
        }
      }
    }

    final StringPartInfo stringPartInfo = mySettings.getStringPartInfo();
    if (stringPartInfo != null) {
      final GrExpression expr =
          GrIntroduceHandlerBase.processLiteral(
              mySettings.getName(), mySettings.getStringPartInfo(), mySettings.getProject());
      final Editor editor = PsiUtilBase.findEditor(expr);
      if (editor != null) {
        editor.getSelectionModel().removeSelection();
        editor.getCaretModel().moveToOffset(expr.getTextRange().getEndOffset());
      }
    }

    final GrVariable var = mySettings.getVar();
    if (var != null && mySettings.removeLocalVariable()) {
      var.delete();
    }
    fieldConflictsResolver.fix();
  }
  private void writeTupleDeclaration(
      GrVariableDeclaration variableDeclaration,
      StringBuilder builder,
      ExpressionContext expressionContext) {
    GrVariable[] variables = variableDeclaration.getVariables();
    final GrExpression tupleInitializer = variableDeclaration.getTupleInitializer();
    if (tupleInitializer instanceof GrListOrMap) {
      for (GrVariable variable : variables) {
        writeVariableSeparately(variable, builder, expressionContext);
        builder.append(";\n");
      }
    } else if (tupleInitializer != null) {

      GroovyResolveResult iteratorMethodResult =
          resolveMethod(
              tupleInitializer,
              "iterator",
              GrExpression.EMPTY_ARRAY,
              GrNamedArgument.EMPTY_ARRAY,
              GrClosableBlock.EMPTY_ARRAY,
              variableDeclaration);

      final PsiType iteratorType = inferIteratorType(iteratorMethodResult, tupleInitializer);

      final String iteratorName =
          genIteratorVar(
              variableDeclaration,
              builder,
              expressionContext,
              tupleInitializer,
              iteratorType,
              iteratorMethodResult);

      final GrModifierList modifierList = variableDeclaration.getModifierList();

      PsiType iterableTypeParameter = PsiUtil.extractIterableTypeParameter(iteratorType, false);

      for (final GrVariable v : variables) {
        ModifierListGenerator.writeModifiers(builder, modifierList);
        final PsiType type = context.typeProvider.getVarType(v);
        writeType(builder, type, variableDeclaration);
        builder.append(' ').append(v.getName());

        builder.append(" = ");
        wrapInCastIfNeeded(
            builder,
            type,
            iterableTypeParameter,
            tupleInitializer,
            expressionContext,
            new StatementWriter() {
              @Override
              public void writeStatement(StringBuilder builder, ExpressionContext context) {
                builder
                    .append(iteratorName)
                    .append(".hasNext() ? ")
                    .append(iteratorName)
                    .append(".next() : null");
              }
            });
        builder.append(";\n");
      }
    } else {
      writeSimpleVarDeclaration(variableDeclaration, builder, expressionContext);
    }
  }
 private static void writeVariableWithoutSemicolonAndInitializer(
     StringBuilder builder, GrVariable var, ExpressionContext context) {
   ModifierListGenerator.writeModifiers(builder, var.getModifierList());
   writeType(builder, context.typeProvider.getVarType(var), var);
   builder.append(' ').append(var.getName());
 }
 public void visitVariable(GrVariable variable) {
   if (myExpression.equals(variable.getInitializerGroovy())) {
     PsiType type = variable.getType();
     myResult = createSimpleSubTypeResult(type);
   }
 }