blob: a94f41155c54839b578455c3d83fc4eb8232d3f1 [file] [log] [blame]
/*
* 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.
*/
package com.intellij.refactoring.introduceParameter;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.IntroduceParameterRefactoring;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
/**
* @author Maxim.Medvedev
* Date: Apr 29, 2009 2:03:38 PM
*/
public class OldReferenceResolver {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceParameter.OldReferenceResolver");
private final PsiCall myContext;
private final PsiExpression myExpr;
private final HashMap<PsiExpression, String> myTempVars;
private final PsiExpression myInstanceRef;
private final PsiExpression[] myActualArgs;
private final PsiMethod myMethodToReplaceIn;
private final Project myProject;
private final PsiManager myManager;
private final int myReplaceFieldsWithGetters;
private final PsiElement myParameterInitializer;
public OldReferenceResolver(PsiCall context,
PsiExpression expr,
PsiMethod methodToReplaceIn,
int replaceFieldsWithGetters,
PsiElement parameterInitializer) throws IncorrectOperationException {
myContext = context;
myExpr = expr;
myReplaceFieldsWithGetters = replaceFieldsWithGetters;
myParameterInitializer = parameterInitializer;
myTempVars = new HashMap<PsiExpression, String>();
myActualArgs = myContext.getArgumentList().getExpressions();
myMethodToReplaceIn = methodToReplaceIn;
myProject = myContext.getProject();
myManager = myContext.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory();
PsiExpression instanceRef;
if (myContext instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)myContext;
final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
instanceRef = methodExpression.getQualifierExpression();
if (instanceRef == null) {
final PsiClass thisResolveClass = RefactoringUtil.getThisResolveClass(methodExpression);
if (thisResolveClass != null &&
!(thisResolveClass instanceof PsiAnonymousClass) &&
!thisResolveClass.equals(PsiTreeUtil.getParentOfType(methodExpression, PsiClass.class))) {
//Qualified this needed
instanceRef = factory.createExpressionFromText(thisResolveClass.getName() + ".this", null);
}
}
}
else {
instanceRef = null;
}
myInstanceRef = instanceRef;
}
public void resolve() throws IncorrectOperationException {
resolveOldReferences(myExpr, myParameterInitializer);
Set<Map.Entry<PsiExpression, String>> mappingsSet = myTempVars.entrySet();
PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory();
for (Map.Entry<PsiExpression, String> entry : mappingsSet) {
PsiExpression oldRef = entry.getKey();
PsiElement newRef = factory.createExpressionFromText(entry.getValue(), null);
oldRef.replace(newRef);
}
}
private void resolveOldReferences(PsiElement expr, PsiElement oldExpr) throws IncorrectOperationException {
if (expr == null || !expr.isValid() || oldExpr == null) return;
PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory();
PsiElement newExpr = expr; // references continue being resolved in the children of newExpr
if (oldExpr instanceof PsiReferenceExpression) {
final PsiReferenceExpression oldRef = (PsiReferenceExpression)oldExpr;
final JavaResolveResult adv = oldRef.advancedResolve(false);
final PsiElement scope = getClassContainingResolve(adv);
final PsiClass clss = PsiTreeUtil.getParentOfType(oldExpr, PsiClass.class);
if (clss != null && scope != null ) {
final PsiElement subj = adv.getElement();
// Parameters
if (subj instanceof PsiParameter) {
PsiParameterList parameterList = myMethodToReplaceIn.getParameterList();
if (subj.getParent() != parameterList) return;
int index = parameterList.getParameterIndex((PsiParameter)subj);
if (index < 0) return;
if (index < myActualArgs.length) {
PsiExpression actualArg = myActualArgs[index];
int copyingSafetyLevel = RefactoringUtil.verifySafeCopyExpression(actualArg);
if (copyingSafetyLevel == RefactoringUtil.EXPR_COPY_PROHIBITED) {
actualArg = factory.createExpressionFromText(getTempVar(actualArg), null);
}
newExpr = newExpr.replace(actualArg);
}
}
// "naked" field and methods (should become qualified)
else if ((subj instanceof PsiField || subj instanceof PsiMethod) && oldRef.getQualifierExpression() == null && PsiTreeUtil.isAncestor(clss, scope, false)) {
boolean isStatic = subj instanceof PsiField && ((PsiField)subj).hasModifierProperty(PsiModifier.STATIC) ||
subj instanceof PsiMethod && ((PsiMethod)subj).hasModifierProperty(PsiModifier.STATIC);
if (myInstanceRef != null && !isStatic) {
String name = ((PsiNamedElement)subj).getName();
PsiReferenceExpression newRef = (PsiReferenceExpression)factory.createExpressionFromText("a." + name, null);
newRef = (PsiReferenceExpression)CodeStyleManager.getInstance(myProject).reformat(newRef);
PsiExpression instanceRef = getInstanceRef(factory);
newRef.getQualifierExpression().replace(instanceRef);
newRef = (PsiReferenceExpression)newExpr.replace(newRef);
newExpr = newRef.getReferenceNameElement();
}
}
if (subj instanceof PsiField && PsiTreeUtil.isAncestor(scope, clss, false)) {
// probably replacing field with a getter
if (myReplaceFieldsWithGetters != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE) {
if (myReplaceFieldsWithGetters == IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_ALL ||
myReplaceFieldsWithGetters == IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE &&
!JavaPsiFacade.getInstance(myProject).getResolveHelper().isAccessible((PsiMember)subj, newExpr, null)) {
newExpr = replaceFieldWithGetter(newExpr, (PsiField)subj, oldRef.getQualifierExpression() == null && !((PsiField)subj).hasModifierProperty(PsiModifier.STATIC));
}
}
}
}
}
else if (oldExpr instanceof PsiThisExpression &&
(((PsiThisExpression)oldExpr).getQualifier() == null ||
myManager
.areElementsEquivalent(((PsiThisExpression)oldExpr).getQualifier().resolve(), myMethodToReplaceIn.getContainingClass()))) {
if (myInstanceRef != null) {
newExpr.replace(getInstanceRef(factory));
}
return;
}
else if (oldExpr instanceof PsiSuperExpression && ((PsiSuperExpression)oldExpr).getQualifier() == null) {
if (myInstanceRef != null) {
newExpr.replace(getInstanceRef(factory));
}
return;
}
PsiElement[] oldChildren = oldExpr.getChildren();
PsiElement[] newChildren = newExpr.getChildren();
if (oldChildren.length == newChildren.length) {
for (int i = 0; i < oldChildren.length; i++) {
resolveOldReferences(newChildren[i], oldChildren[i]);
}
}
}
private PsiExpression getInstanceRef(PsiElementFactory factory) throws IncorrectOperationException {
int copyingSafetyLevel = RefactoringUtil.verifySafeCopyExpression(myInstanceRef);
PsiExpression instanceRef = myInstanceRef;
if (copyingSafetyLevel == RefactoringUtil.EXPR_COPY_PROHIBITED) {
instanceRef = factory.createExpressionFromText(getTempVar(myInstanceRef), null);
}
return instanceRef;
}
private String getTempVar(PsiExpression expr) throws IncorrectOperationException {
String id = myTempVars.get(expr);
if (id != null) {
return id;
}
else {
id = RefactoringUtil.createTempVar(expr, myContext, true);
myTempVars.put(expr, id);
return id;
}
}
private PsiElement replaceFieldWithGetter(PsiElement expr, PsiField psiField, boolean qualify) throws IncorrectOperationException {
if (RefactoringUtil.isAssignmentLHS(expr)) {
// todo: warning
return expr;
}
PsiElement newExpr = expr;
PsiMethod getterPrototype = GenerateMembersUtil.generateGetterPrototype(psiField);
PsiMethod getter = psiField.getContainingClass().findMethodBySignature(getterPrototype, true);
if (getter != null) {
if (JavaPsiFacade.getInstance(psiField.getProject()).getResolveHelper().isAccessible(getter, newExpr, null)) {
PsiElementFactory factory = JavaPsiFacade.getInstance(newExpr.getProject()).getElementFactory();
String id = getter.getName();
String qualifier = null;
if (newExpr instanceof PsiReferenceExpression) {
final PsiExpression instanceRef = getInstanceRef(factory);
if (qualify && instanceRef != null) {
qualifier = instanceRef.getText();
} else {
final PsiExpression qualifierExpression = ((PsiReferenceExpression)newExpr).getQualifierExpression();
if (qualifierExpression != null) {
qualifier = qualifierExpression.getText();
}
}
}
PsiMethodCallExpression getterCall =
(PsiMethodCallExpression)factory.createExpressionFromText((qualifier != null ? qualifier + "." : "") + id + "()", null);
getterCall = (PsiMethodCallExpression)CodeStyleManager.getInstance(myProject).reformat(getterCall);
if (newExpr.getParent() != null) {
newExpr = newExpr.replace(getterCall);
}
else {
newExpr = getterCall;
}
}
else {
// todo: warning
}
}
return newExpr;
}
@Nullable
private static PsiElement getClassContainingResolve(final JavaResolveResult result) {
final PsiElement elem = result.getElement();
if (elem != null) {
if (elem instanceof PsiLocalVariable || elem instanceof PsiParameter) {
return PsiTreeUtil.getParentOfType(elem, PsiClass.class);
}
else {
return result.getCurrentFileResolveScope();
}
}
return null;
}
}