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

com.intellij.codeInsight.daemon.impl.quickfix.DeferFinalAssignmentFix Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition java-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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.
 */

/**
 * Created by IntelliJ IDEA.
 * User: cdr
 * Date: Nov 19, 2002
 * Time: 12:03:39 PM
 * To change this template use Options | File Templates.
 */
package com.intellij.codeInsight.daemon.impl.quickfix;

import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

public class DeferFinalAssignmentFix implements IntentionAction {
  private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.DeferFinalAssignmentFix");

  private final PsiVariable variable;
  private final PsiReferenceExpression expression;

  public DeferFinalAssignmentFix(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
    this.variable = variable;
    this.expression = expression;
  }

  @Override
  @NotNull
  public String getFamilyName() {
    return QuickFixBundle.message("defer.final.assignment.with.temp.family");
  }

  @Override
  @NotNull
  public String getText() {
    return QuickFixBundle.message("defer.final.assignment.with.temp.text", variable.getName());
  }

  @Override
  public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
    if (!FileModificationService.getInstance().prepareFileForWrite(variable.getContainingFile())) return;

    if (variable instanceof PsiField) {
      deferField((PsiField)variable);
    }
    else {
      deferLocalVariable((PsiLocalVariable)variable);
    }
  }

  private void deferField(PsiField field) throws IncorrectOperationException {
    PsiCodeBlock codeBlock = getEnclosingCodeBlock(field, expression);
    if (codeBlock == null) return;
    deferVariable(codeBlock, field, null);
  }

  private static PsiCodeBlock getEnclosingCodeBlock(PsiField field, PsiElement element) {
    PsiClass aClass = field.getContainingClass();
    if (aClass == null) return null;
    PsiMethod[] constructors = aClass.getConstructors();
    for (PsiMethod constructor : constructors) {
      PsiCodeBlock body = constructor.getBody();
      if (body == null) continue;
      if (PsiTreeUtil.isAncestor(body, element, true)) return body;
    }

    //maybe inside class initalizer ?
    PsiClassInitializer[] initializers = aClass.getInitializers();
    for (PsiClassInitializer initializer : initializers) {
      PsiCodeBlock body = initializer.getBody();
      if (PsiTreeUtil.isAncestor(body, element, true)) return body;
    }
    return null;
  }

  private void deferLocalVariable(PsiLocalVariable variable) throws IncorrectOperationException {
    PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
    deferVariable(outerCodeBlock, variable, variable.getParent());
  }

  private void deferVariable(PsiElement outerCodeBlock, PsiVariable variable, PsiElement tempDeclarationAnchor) throws IncorrectOperationException {
    if (outerCodeBlock == null) return;
    List outerReferences = new ArrayList();
    collectReferences(outerCodeBlock, variable, outerReferences);

    PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
    Project project = variable.getProject();
    String tempName = suggestNewName(project, variable);
    PsiDeclarationStatement tempVariableDeclaration = factory.createVariableDeclarationStatement(tempName, variable.getType(), null);

    ControlFlow controlFlow;
    try {
      controlFlow = ControlFlowFactory.getInstance(project).getControlFlow(outerCodeBlock, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
    }
    catch (AnalysisCanceledException e) {
      return;
    }
    int minOffset = 0;
    boolean writeReferenceOccurred = false;
    PsiReferenceExpression writeReference = null;
    for (int i = outerReferences.size()-1; i>=0; i--) {
      PsiReferenceExpression reference = outerReferences.get(i);
      if (!writeReferenceOccurred && !PsiUtil.isAccessedForWriting(reference)) {
        // trailing read references need not be converted to temp var references
        outerReferences.remove(i);
        continue;
      }
      writeReferenceOccurred = true;
      writeReference = reference;
      PsiElement element = PsiUtil.getEnclosingStatement(reference);
      int endOffset = element == null ? -1 : controlFlow.getEndOffset(element);
      minOffset = Math.max(minOffset, endOffset);
    }
    LOG.assertTrue(writeReference != null);
    PsiStatement finalAssignment = factory.createStatementFromText(writeReference.getText()+" = "+tempName+";", outerCodeBlock);
    if (!insertToDefinitelyReachedPlace(outerCodeBlock, finalAssignment, controlFlow, minOffset, outerReferences)) return;

    outerCodeBlock.addAfter(tempVariableDeclaration, tempDeclarationAnchor);

    replaceReferences(outerReferences, factory.createExpressionFromText(tempName, outerCodeBlock));
  }


  private static boolean insertToDefinitelyReachedPlace(PsiElement codeBlock,
                                                        PsiStatement finalAssignment,
                                                        ControlFlow controlFlow,
                                                        int minOffset,
                                                        List references) throws IncorrectOperationException {
    int offset = ControlFlowUtil.getMinDefinitelyReachedOffset(controlFlow, minOffset, references);
    if (offset == controlFlow.getSize()) {
      codeBlock.add(finalAssignment);
      return true;
    }
    PsiElement element = null; //controlFlow.getEndOffset(codeBlock) == offset ? getEnclosingStatement(controlFlow.getElement(offset)) : null;
    while (offset < controlFlow.getSize()) {
      element = controlFlow.getElement(offset);
      if (element != null) element = PsiUtil.getEnclosingStatement(element);
      int startOffset = controlFlow.getStartOffset(element);
      if (startOffset != -1 && startOffset >= minOffset && element instanceof PsiStatement) break;
      offset++;
    }
    if (!(offset < controlFlow.getSize())) return false;
    // inside loop
    if (ControlFlowUtil.isInstructionReachable(controlFlow, offset, offset)) return false;
    codeBlock.addBefore(finalAssignment, element);
    return true;
  }

  private static void replaceReferences(List references, PsiElement newExpression) throws IncorrectOperationException {
    for (Object reference1 : references) {
      PsiElement reference = (PsiElement)reference1;
      reference.replace(newExpression);
    }


  }

  private static void collectReferences(PsiElement context, final PsiVariable variable, final List references) {
    context.accept(new JavaRecursiveElementWalkingVisitor() {
      @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
        if (expression.resolve() == variable) references.add(expression);
        super.visitReferenceExpression(expression);
      }
    });
  }

  private static String suggestNewName(Project project, PsiVariable variable) {
    // new name should not conflict with another variable at the variable declaration level and usage level
    String name = variable.getName();
    // trim last digit to suggest variable names like i1,i2, i3...
    if (name.length() > 1 && Character.isDigit(name.charAt(name.length()-1))) {
      name = name.substring(0,name.length()-1);
    }
    return JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(name, variable, true);
  }

  @Override
  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
    return
      variable.isValid() &&
      !(variable instanceof PsiParameter) &&
      !(variable instanceof ImplicitVariable) &&
      expression.isValid() &&
      variable.getManager().isInProject(variable)
        ;
  }

  @Override
  public boolean startInWriteAction() {
    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy