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

com.intellij.refactoring.move.moveInner.MoveInnerProcessor 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-2015 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.
 */
package com.intellij.refactoring.move.moveInner;

import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.ide.util.EditorHelper;
import com.intellij.lang.findUsages.DescriptiveNameUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesUtil;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.NonCodeUsageInfo;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * created at Sep 24, 2001
 * @author Jeka
 */
public class MoveInnerProcessor extends BaseRefactoringProcessor {
  private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.move.moveInner.MoveInnerProcessor");

  private MoveCallback myMoveCallback;

  private PsiClass myInnerClass;
  private PsiClass myOuterClass;
  private PsiElement myTargetContainer;
  private String myParameterNameOuterClass;
  private String myFieldNameOuterClass;
  private String myDescriptiveName = "";
  private String myNewClassName;
  private boolean mySearchInComments;
  private boolean mySearchInNonJavaFiles;
  private NonCodeUsageInfo[] myNonCodeUsages;
  private boolean myOpenInEditor;

  public MoveInnerProcessor(Project project, MoveCallback moveCallback) {
    super(project);
    myMoveCallback = moveCallback;
  }

  public MoveInnerProcessor(Project project,
                            PsiClass innerClass,
                            String name,
                            boolean passOuterClass,
                            String parameterName,
                            final PsiElement targetContainer) {
    super(project);
    setup(innerClass, name, passOuterClass, parameterName, true, true, targetContainer);
  }

  protected String getCommandName() {
    return RefactoringBundle.message("move.inner.class.command", myDescriptiveName);
  }

  @NotNull
  protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usages) {
    return new MoveInnerViewDescriptor(myInnerClass);
  }

  @NotNull
  protected UsageInfo[] findUsages() {
    LOG.assertTrue(myTargetContainer != null);

    Collection innerClassRefs = ReferencesSearch.search(myInnerClass).findAll();
    ArrayList usageInfos = new ArrayList(innerClassRefs.size());
    for (PsiReference innerClassRef : innerClassRefs) {
      PsiElement ref = innerClassRef.getElement();
      if (!PsiTreeUtil.isAncestor(myInnerClass, ref, true)) { // do not show self-references
        usageInfos.add(new UsageInfo(ref));
      }
    }

    final String newQName;
    if (myTargetContainer instanceof PsiDirectory) {
      final PsiDirectory targetDirectory = (PsiDirectory)myTargetContainer;
      final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(targetDirectory);
      LOG.assertTrue(aPackage != null);
      newQName = aPackage.getQualifiedName() + "." + myNewClassName;
    }
    else if (myTargetContainer instanceof PsiClass) {
      final String qName = ((PsiClass)myTargetContainer).getQualifiedName();
      if (qName != null) {
        newQName = qName + "." + myNewClassName;
      }
      else {
        newQName = myNewClassName;
      }
    }
    else {
      newQName = myNewClassName;
    }
    MoveClassesOrPackagesUtil.findNonCodeUsages(mySearchInComments, mySearchInNonJavaFiles,
                                                myInnerClass, newQName, usageInfos);
    return usageInfos.toArray(new UsageInfo[usageInfos.size()]);
  }

  protected void refreshElements(@NotNull PsiElement[] elements) {
    boolean condition = elements.length == 1 && elements[0] instanceof PsiClass;
    LOG.assertTrue(condition);
    myInnerClass = (PsiClass)elements[0];
  }

  public boolean isSearchInComments() {
    return mySearchInComments;
  }

  public void setSearchInComments(boolean searchInComments) {
    mySearchInComments = searchInComments;
  }

  public boolean isSearchInNonJavaFiles() {
    return mySearchInNonJavaFiles;
  }

  public void setSearchInNonJavaFiles(boolean searchInNonJavaFiles) {
    mySearchInNonJavaFiles = searchInNonJavaFiles;
  }

  protected void performRefactoring(@NotNull final UsageInfo[] usages) {
    final PsiManager manager = PsiManager.getInstance(myProject);
    final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();

    final RefactoringElementListener elementListener = getTransaction().getElementListener(myInnerClass);
    try {
      PsiField field = null;
      if (myParameterNameOuterClass != null) {
        // pass outer as a parameter
        field = factory.createField(myFieldNameOuterClass, factory.createType(myOuterClass));
        field = addOuterField(field);
        myInnerClass = field.getContainingClass();
        addFieldInitializationToConstructors(myInnerClass, field, myParameterNameOuterClass);
      }

      ChangeContextUtil.encodeContextInfo(myInnerClass, false);

      myInnerClass = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myInnerClass);

      final MoveInnerOptions moveInnerOptions = new MoveInnerOptions(myInnerClass, myOuterClass, myTargetContainer, myNewClassName);
      final MoveInnerHandler handler = MoveInnerHandler.EP_NAME.forLanguage(myInnerClass.getLanguage());
      final PsiClass newClass;
      try {
        newClass = handler.copyClass(moveInnerOptions);
      }
      catch (IncorrectOperationException e) {
        RefactoringUIUtil.processIncorrectOperation(myProject, e);
        return;
      }

      // replace references in a new class to old inner class with references to itself
      for (PsiReference ref : ReferencesSearch.search(myInnerClass, new LocalSearchScope(newClass), true)) {
        PsiElement element = ref.getElement();
        if (element.getParent() instanceof PsiJavaCodeReferenceElement) {
          PsiJavaCodeReferenceElement parentRef = (PsiJavaCodeReferenceElement)element.getParent();
          PsiElement parentRefElement = parentRef.resolve();
          if (parentRefElement instanceof PsiClass) { // reference to inner class inside our inner
            final PsiReferenceList referenceList = PsiTreeUtil.getTopmostParentOfType(parentRef, PsiReferenceList.class);
            if (referenceList == null || referenceList.getParent() != newClass) {
              parentRef.getQualifier().delete();
              continue;
            }
          }
        }
        ref.bindToElement(newClass);
      }

      List referencesToRebind = new ArrayList();
      for (UsageInfo usage : usages) {
        if (usage.isNonCodeUsage) continue;
        PsiElement refElement = usage.getElement();
        PsiReference[] references = refElement.getReferences();
        for (PsiReference reference : references) {
          if (reference.isReferenceTo(myInnerClass)) {
            referencesToRebind.add(reference);
          }
        }
      }

      myInnerClass.delete();

      // correct references in usages
      for (UsageInfo usage : usages) {
        if (usage.isNonCodeUsage || myParameterNameOuterClass == null) continue; // should pass outer as parameter

        MoveInnerClassUsagesHandler usagesHandler = MoveInnerClassUsagesHandler.EP_NAME.forLanguage(usage.getElement().getLanguage());
        if (usagesHandler != null) {
          usagesHandler.correctInnerClassUsage(usage, myOuterClass);
        }
      }

      for (PsiReference reference : referencesToRebind) {
        reference.bindToElement(newClass);
      }

      for (UsageInfo usage : usages) {
        final PsiElement element = usage.getElement();
        final PsiElement parent = element != null ? element.getParent() : null;
        if (parent instanceof PsiNewExpression) {
          final PsiMethod resolveConstructor = ((PsiNewExpression)parent).resolveConstructor();
          for (PsiMethod method : newClass.getConstructors()) {
            if (resolveConstructor == method) {
              final PsiElement place = usage.getElement();
              if (place != null) {
                VisibilityUtil.escalateVisibility(method, place);
              }
              break;
            }
          }
        }
      }

      if (field != null) {
        final PsiExpression paramAccessExpression = factory.createExpressionFromText(myParameterNameOuterClass, null);
        for (final PsiMethod constructor : newClass.getConstructors()) {
          final PsiStatement[] statements = constructor.getBody().getStatements();
          if (statements.length > 0) {
            if (statements[0] instanceof PsiExpressionStatement) {
              PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
              if (expression instanceof PsiMethodCallExpression) {
                @NonNls String text = ((PsiMethodCallExpression)expression).getMethodExpression().getText();
                if ("this".equals(text) || "super".equals(text)) {
                  ChangeContextUtil.decodeContextInfo(expression, myOuterClass, paramAccessExpression);
                }
              }
            }
          }
        }

        PsiExpression accessExpression = factory.createExpressionFromText(myFieldNameOuterClass, null);
        ChangeContextUtil.decodeContextInfo(newClass, myOuterClass, accessExpression);
      }
      else {
        ChangeContextUtil.decodeContextInfo(newClass, null, null);
      }

      if (myOpenInEditor) {
        EditorHelper.openInEditor(newClass);
      }

      if (myMoveCallback != null) {
        myMoveCallback.refactoringCompleted();
      }
      elementListener.elementMoved(newClass);

      List nonCodeUsages = new ArrayList();
      for (UsageInfo usage : usages) {
        if (usage instanceof NonCodeUsageInfo) {
          nonCodeUsages.add((NonCodeUsageInfo)usage);
        }
      }
      myNonCodeUsages = nonCodeUsages.toArray(new NonCodeUsageInfo[nonCodeUsages.size()]);
    }
    catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }

  private PsiField addOuterField(PsiField field) {
    final PsiMember[] members = PsiTreeUtil.getChildrenOfType(myInnerClass, PsiMember.class);
    if (members != null) {
      for (PsiMember member : members) {
        if (!member.hasModifierProperty(PsiModifier.STATIC)) {
          return (PsiField)myInnerClass.addBefore(field, member);
        }
      }
    }

    return (PsiField)myInnerClass.add(field);
  }

  protected void performPsiSpoilingRefactoring() {
    if (myNonCodeUsages != null) {
      RenameUtil.renameNonCodeUsages(myProject, myNonCodeUsages);
    }
  }

  protected boolean preprocessUsages(@NotNull Ref refUsages) {
    final MultiMap conflicts = new MultiMap();
    final HashMap> reported = new HashMap>();
    class Visitor extends JavaRecursiveElementWalkingVisitor {


      @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
        PsiElement resolved = reference.resolve();
        if (resolved instanceof PsiMember &&
            PsiTreeUtil.isAncestor(myInnerClass, resolved, true) &&
            becomesInaccessible((PsiMember)resolved)) {
          registerConflict(reference, resolved, reported, conflicts);
        }
      }




      @Override public void visitClass(PsiClass aClass) {
        if (aClass == myInnerClass) return;
        super.visitClass(aClass);
      }
    }

//    if (myInnerClass.hasModifierProperty(PsiModifier.)) {
    myOuterClass.accept(new Visitor());
    myInnerClass.accept(new JavaRecursiveElementWalkingVisitor() {
      @Override
      public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
        super.visitReferenceElement(reference);
        final PsiElement resolve = reference.resolve();
        if (resolve instanceof PsiMember) {
          if (PsiTreeUtil.isAncestor(myOuterClass, resolve, true) && !PsiTreeUtil.isAncestor(myInnerClass, resolve, false)) {
            if (becomesInaccessible((PsiMember)resolve)) {
              registerConflict(reference, resolve, reported, conflicts);
            }
          }
        }
      }
    });

    return showConflicts(conflicts, refUsages.get());
  }

  private static void registerConflict(PsiJavaCodeReferenceElement reference,
                                       PsiElement resolved,
                                       HashMap> reported, MultiMap conflicts) {
    final PsiElement container = ConflictsUtil.getContainer(reference);
    HashSet containerSet = reported.get(container);
    if (containerSet == null) {
      containerSet = new HashSet();
      reported.put(container, containerSet);
    }
    if (!containerSet.contains(resolved)) {
      containerSet.add(resolved);
      String placesDescription;
      if (containerSet.size() == 1) {
        placesDescription = RefactoringUIUtil.getDescription(resolved, true);
      } else {
        placesDescription = "
  1. " + StringUtil.join(containerSet, new Function() { @Override public String fun(PsiElement element) { return RefactoringUIUtil.getDescription(element, true); } }, "
  2. ") + "
"; } String message = RefactoringBundle.message("0.will.become.inaccessible.from.1", placesDescription, RefactoringUIUtil.getDescription(container, true)); conflicts.put(container, Collections.singletonList(message)); } } private boolean becomesInaccessible(PsiMember element) { final String visibilityModifier = VisibilityUtil.getVisibilityModifier(element.getModifierList()); if (PsiModifier.PRIVATE.equals(visibilityModifier)) return true; if (PsiModifier.PUBLIC.equals(visibilityModifier)) return false; final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject); if (myTargetContainer instanceof PsiDirectory) { final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)myTargetContainer); assert aPackage != null : myTargetContainer; return !psiFacade.isInPackage(myOuterClass, aPackage); } // target container is a class PsiFile targetFile = myTargetContainer.getContainingFile(); if (targetFile != null) { final PsiDirectory containingDirectory = targetFile.getContainingDirectory(); if (containingDirectory != null) { final PsiPackage targetPackage = JavaDirectoryService.getInstance().getPackage(containingDirectory); assert targetPackage != null : myTargetContainer; return psiFacade.isInPackage(myOuterClass, targetPackage); } } return false; } public void setup(final PsiClass innerClass, final String className, final boolean passOuterClass, final String parameterName, boolean searchInComments, boolean searchInNonJava, @NotNull final PsiElement targetContainer) { myNewClassName = className; myInnerClass = innerClass; myDescriptiveName = DescriptiveNameUtil.getDescriptiveName(myInnerClass); myOuterClass = myInnerClass.getContainingClass(); myTargetContainer = targetContainer; JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(myProject); myParameterNameOuterClass = passOuterClass ? parameterName : null; if (myParameterNameOuterClass != null) { myFieldNameOuterClass = codeStyleManager.variableNameToPropertyName(myParameterNameOuterClass, VariableKind.PARAMETER); myFieldNameOuterClass = codeStyleManager.propertyNameToVariableName(myFieldNameOuterClass, VariableKind.FIELD); } mySearchInComments = searchInComments; mySearchInNonJavaFiles = searchInNonJava; } private void addFieldInitializationToConstructors(PsiClass aClass, PsiField field, String parameterName) throws IncorrectOperationException { PsiMethod[] constructors = aClass.getConstructors(); PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory(); if (constructors.length > 0) { for (PsiMethod constructor : constructors) { if (parameterName != null) { PsiParameterList parameterList = constructor.getParameterList(); PsiParameter parameter = factory.createParameter(parameterName, field.getType()); parameterList.addAfter(parameter, null); } PsiCodeBlock body = constructor.getBody(); if (body == null) continue; PsiStatement[] statements = body.getStatements(); if (statements.length > 0) { PsiStatement first = statements[0]; if (first instanceof PsiExpressionStatement) { PsiExpression expression = ((PsiExpressionStatement)first).getExpression(); if (expression instanceof PsiMethodCallExpression) { @NonNls String text = ((PsiMethodCallExpression)expression).getMethodExpression().getText(); if ("this".equals(text)) { continue; } } } } createAssignmentStatement(constructor, field.getName(), parameterName); } } else { PsiMethod constructor = factory.createConstructor(); if (parameterName != null) { PsiParameterList parameterList = constructor.getParameterList(); PsiParameter parameter = factory.createParameter(parameterName, field.getType()); parameterList.add(parameter); } createAssignmentStatement(constructor, field.getName(), parameterName); aClass.add(constructor); } } private PsiStatement createAssignmentStatement(PsiMethod constructor, String fieldName, String parameterName) throws IncorrectOperationException { PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory(); @NonNls String pattern = fieldName + "=a;"; if (fieldName.equals(parameterName)) { pattern = "this." + pattern; } PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(pattern, null); statement = (PsiExpressionStatement)CodeStyleManager.getInstance(myProject).reformat(statement); PsiCodeBlock body = constructor.getBody(); assert body != null : constructor; statement = (PsiExpressionStatement)body.addAfter(statement, getAnchorElement(body)); PsiAssignmentExpression assignment = (PsiAssignmentExpression)statement.getExpression(); PsiReferenceExpression rExpr = (PsiReferenceExpression)assignment.getRExpression(); assert rExpr != null : assignment; PsiIdentifier identifier = (PsiIdentifier)rExpr.getReferenceNameElement(); assert identifier != null : assignment; identifier.replace(factory.createIdentifier(parameterName)); return statement; } @Nullable private static PsiElement getAnchorElement(PsiCodeBlock body) { PsiStatement[] statements = body.getStatements(); if (statements.length > 0) { PsiStatement first = statements[0]; if (first instanceof PsiExpressionStatement) { PsiExpression expression = ((PsiExpressionStatement)first).getExpression(); if (expression instanceof PsiMethodCallExpression) { PsiReferenceExpression methodCall = ((PsiMethodCallExpression)expression).getMethodExpression(); @NonNls String text = methodCall.getText(); if ("super".equals(text)) { return first; } } } } return null; } public PsiClass getInnerClass() { return myInnerClass; } public String getNewClassName() { return myNewClassName; } public boolean shouldPassParameter() { return myParameterNameOuterClass != null; } public String getParameterName() { return myParameterNameOuterClass; } public void setOpenInEditor(boolean openInEditor) { myOpenInEditor = openInEditor; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy