blob: 5672203e3c170ee4bde8a77bb111c994c713846c [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.makeStatic;
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.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.move.MoveInstanceMembersUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import java.util.ArrayList;
import java.util.List;
/**
* @author ven
*/
public class MakeClassStaticProcessor extends MakeMethodOrClassStaticProcessor<PsiClass> {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.makeMethodStatic.MakeClassStaticProcessor");
private List<PsiField> myFieldsToSplit = new ArrayList<PsiField>();
public MakeClassStaticProcessor(final Project project, final PsiClass aClass, final Settings settings) {
super(project, aClass, settings);
}
protected void changeSelf(final PsiElementFactory factory, final UsageInfo[] usages) throws IncorrectOperationException {
PsiClass containingClass = myMember.getContainingClass();
//Add fields
if (mySettings.isMakeClassParameter()) {
PsiType type = factory.createType(containingClass, PsiSubstitutor.EMPTY);
final String classParameterName = mySettings.getClassParameterName();
final String fieldName = convertToFieldName(classParameterName);
myMember.add(factory.createField(fieldName, type));
}
if (mySettings.isMakeFieldParameters()) {
List<Settings.FieldParameter> parameters = mySettings.getParameterOrderList();
for (Settings.FieldParameter fieldParameter : parameters) {
final PsiType type = fieldParameter.type;
final PsiField field = factory.createField(convertToFieldName(fieldParameter.name), type);
myMember.add(field);
}
}
PsiMethod[] constructors = myMember.getConstructors();
if (constructors.length == 0) {
final PsiMethod defConstructor = (PsiMethod)myMember.add(factory.createConstructor());
constructors = new PsiMethod[]{defConstructor};
}
boolean generateFinalParams = CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS;
for (PsiMethod constructor : constructors) {
final MethodJavaDocHelper javaDocHelper = new MethodJavaDocHelper(constructor);
PsiParameterList paramList = constructor.getParameterList();
PsiElement addParameterAfter = null;
PsiDocTag anchor = null;
if (mySettings.isMakeClassParameter()) {
// Add parameter for object
PsiType parameterType = factory.createType(containingClass, PsiSubstitutor.EMPTY);
final String classParameterName = mySettings.getClassParameterName();
PsiParameter parameter = factory.createParameter(classParameterName, parameterType);
PsiUtil.setModifierProperty(parameter, PsiModifier.FINAL, makeClassParameterFinal(usages) || generateFinalParams);
addParameterAfter = paramList.addAfter(parameter, null);
anchor = javaDocHelper.addParameterAfter(classParameterName, anchor);
addAssignmentToField(classParameterName, constructor);
}
if (mySettings.isMakeFieldParameters()) {
List<Settings.FieldParameter> parameters = mySettings.getParameterOrderList();
for (Settings.FieldParameter fieldParameter : parameters) {
final PsiType fieldParameterType = fieldParameter.field.getType();
final PsiParameter parameter = factory.createParameter(fieldParameter.name, fieldParameterType);
PsiUtil.setModifierProperty(parameter, PsiModifier.FINAL,
makeFieldParameterFinal(fieldParameter.field, usages) || generateFinalParams);
addParameterAfter = paramList.addAfter(parameter, addParameterAfter);
anchor = javaDocHelper.addParameterAfter(fieldParameter.name, anchor);
addAssignmentToField(fieldParameter.name, constructor);
}
for (UsageInfo usage : usages) {
if (usage instanceof InternalUsageInfo) {
final PsiElement element = usage.getElement();
final PsiElement referencedElement = ((InternalUsageInfo)usage).getReferencedElement();
if (referencedElement instanceof PsiField && mySettings.getNameForField((PsiField)referencedElement) != null) {
final PsiField field = PsiTreeUtil.getParentOfType(element, PsiField.class);
if (field != null) {
MoveInstanceMembersUtil.moveInitializerToConstructor(factory, constructor, field);
}
}
}
}
}
for (PsiField field : myFieldsToSplit) {
MoveInstanceMembersUtil.moveInitializerToConstructor(factory, constructor, field);
}
}
setupTypeParameterList();
// Add static modifier
final PsiModifierList modifierList = myMember.getModifierList();
modifierList.setModifierProperty(PsiModifier.STATIC, true);
modifierList.setModifierProperty(PsiModifier.FINAL, false);
}
private void addAssignmentToField(final String parameterName, final PsiMethod constructor) {
@NonNls String fieldName = convertToFieldName(parameterName);
final PsiManager manager = PsiManager.getInstance(myProject);
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
final PsiCodeBlock body = constructor.getBody();
if (body != null) {
try {
final PsiReferenceExpression refExpr = (PsiReferenceExpression)factory.createExpressionFromText(fieldName, body);
if (refExpr.resolve() != null) fieldName = "this." + fieldName;
PsiStatement statement = factory.createStatementFromText(fieldName + "=" + parameterName + ";", null);
statement = (PsiStatement)CodeStyleManager.getInstance(manager.getProject()).reformat(statement);
body.add(statement);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
private String convertToFieldName(final String parameterName) {
JavaCodeStyleManager manager = JavaCodeStyleManager.getInstance(myProject);
final String propertyName = manager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER);
final String fieldName = manager.propertyNameToVariableName(propertyName, VariableKind.FIELD);
return fieldName;
}
protected void changeSelfUsage(final SelfUsageInfo usageInfo) throws IncorrectOperationException {
PsiElement parent = usageInfo.getElement().getParent();
LOG.assertTrue(parent instanceof PsiCallExpression); //either this() or new()
PsiCallExpression call = (PsiCallExpression) parent;
PsiElementFactory factory = JavaPsiFacade.getInstance(call.getProject()).getElementFactory();
PsiExpressionList args = call.getArgumentList();
PsiElement addParameterAfter = null;
if(mySettings.isMakeClassParameter()) {
PsiElement arg = factory.createExpressionFromText(convertToFieldName(mySettings.getClassParameterName()), null);
addParameterAfter = args.addAfter(arg, null);
}
if(mySettings.isMakeFieldParameters()) {
List<Settings.FieldParameter> parameters = mySettings.getParameterOrderList();
for (Settings.FieldParameter fieldParameter : parameters) {
PsiElement arg = factory.createExpressionFromText(convertToFieldName(fieldParameter.name), null);
if (addParameterAfter == null) {
addParameterAfter = args.addAfter(arg, null);
}
else {
addParameterAfter = args.addAfter(arg, addParameterAfter);
}
}
}
}
protected void changeInternalUsage(final InternalUsageInfo usage, final PsiElementFactory factory) throws IncorrectOperationException {
if (!mySettings.isChangeSignature()) return;
PsiElement element = usage.getElement();
if (element instanceof PsiReferenceExpression) {
PsiReferenceExpression newRef = null;
if (mySettings.isMakeFieldParameters()) {
PsiElement resolved = ((PsiReferenceExpression) element).resolve();
if (resolved instanceof PsiField) {
String name = mySettings.getNameForField((PsiField)resolved);
if (name != null) {
name = convertToFieldName(name);
if (name != null) {
newRef = (PsiReferenceExpression) factory.createExpressionFromText(name, null);
}
}
}
}
if (newRef == null && mySettings.isMakeClassParameter()) {
newRef =
(PsiReferenceExpression) factory.createExpressionFromText(
convertToFieldName(mySettings.getClassParameterName()) + "." + element.getText(), null);
}
if (newRef != null) {
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myProject);
newRef = (PsiReferenceExpression) codeStyleManager.reformat(newRef);
element.replace(newRef);
}
}
else if (mySettings.isMakeClassParameter() && (element instanceof PsiThisExpression || element instanceof PsiSuperExpression)) {
final PsiElement replace =
element.replace(factory.createExpressionFromText(convertToFieldName(mySettings.getClassParameterName()), null));
final PsiField field = PsiTreeUtil.getParentOfType(replace, PsiField.class);
if (field != null) {
myFieldsToSplit.add(field);
}
}
else if (element instanceof PsiNewExpression && mySettings.isMakeClassParameter()) {
final PsiNewExpression newExpression = ((PsiNewExpression)element);
LOG.assertTrue(newExpression.getQualifier() == null);
final String newText = convertToFieldName(mySettings.getClassParameterName()) + "." + newExpression.getText();
final PsiExpression expr = factory.createExpressionFromText(newText, null);
element.replace(expr);
}
}
protected void changeExternalUsage(final UsageInfo usage, final PsiElementFactory factory) throws IncorrectOperationException {
final PsiElement element = usage.getElement();
if (!(element instanceof PsiJavaCodeReferenceElement)) return;
PsiJavaCodeReferenceElement methodRef = (PsiJavaCodeReferenceElement)element;
PsiElement parent = methodRef.getParent();
if (parent instanceof PsiAnonymousClass) {
parent = parent.getParent();
}
LOG.assertTrue(parent instanceof PsiCallExpression, "call expression expected, found " + parent);
PsiCallExpression call = (PsiCallExpression)parent;
PsiExpression instanceRef;
instanceRef = call instanceof PsiMethodCallExpression ? ((PsiMethodCallExpression)call).getMethodExpression().getQualifierExpression() :
((PsiNewExpression)call).getQualifier();
PsiElement newQualifier;
if (instanceRef == null || instanceRef instanceof PsiSuperExpression) {
final PsiClass thisClass = RefactoringChangeUtil.getThisClass(element);
@NonNls String thisText;
if (thisClass.getManager().areElementsEquivalent(thisClass, myMember.getContainingClass())) {
thisText = "this";
}
else {
thisText = myMember.getContainingClass().getName() + ".this";
}
instanceRef = factory.createExpressionFromText(thisText, null);
newQualifier = null;
}
else {
newQualifier = factory.createReferenceExpression(myMember.getContainingClass());
}
if (mySettings.getNewParametersNumber() > 1) {
int copyingSafetyLevel = RefactoringUtil.verifySafeCopyExpression(instanceRef);
if (copyingSafetyLevel == RefactoringUtil.EXPR_COPY_PROHIBITED) {
String tempVar = RefactoringUtil.createTempVar(instanceRef, call, true);
instanceRef = factory.createExpressionFromText(tempVar, null);
}
}
PsiElement anchor = null;
PsiExpressionList argList = call.getArgumentList();
PsiExpression[] exprs = argList.getExpressions();
if (mySettings.isMakeClassParameter()) {
if (exprs.length > 0) {
anchor = argList.addBefore(instanceRef, exprs[0]);
}
else {
anchor = argList.add(instanceRef);
}
}
if (mySettings.isMakeFieldParameters()) {
List<Settings.FieldParameter> parameters = mySettings.getParameterOrderList();
for (Settings.FieldParameter fieldParameter : parameters) {
PsiReferenceExpression fieldRef;
final String fieldName = fieldParameter.field.getName();
if (newQualifier != null) {
fieldRef = (PsiReferenceExpression)factory.createExpressionFromText(
"a." + fieldName, null);
fieldRef.getQualifierExpression().replace(instanceRef);
}
else {
fieldRef = (PsiReferenceExpression)factory.createExpressionFromText(fieldName, null);
}
if (anchor != null) {
anchor = argList.addAfter(fieldRef, anchor);
}
else {
if (exprs.length > 0) {
anchor = argList.addBefore(fieldRef, exprs[0]);
}
else {
anchor = argList.add(fieldRef);
}
}
}
}
if (newQualifier != null) {
if (call instanceof PsiMethodCallExpression) {
instanceRef.replace(newQualifier);
} else {
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)call).getAnonymousClass();
if (anonymousClass != null) {
((PsiNewExpression)call).getQualifier().delete();
final PsiJavaCodeReferenceElement baseClassReference = anonymousClass.getBaseClassReference();
baseClassReference.replace(((PsiNewExpression)factory.createExpressionFromText("new " + newQualifier.getText() + "." + baseClassReference.getText() + "()", baseClassReference)).getClassReference());
} else {
PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)call).getClassReference();
LOG.assertTrue(classReference != null);
final PsiNewExpression newExpr =
(PsiNewExpression)factory.createExpressionFromText("new " + newQualifier.getText() + "." + classReference.getText() + "()", classReference);
final PsiExpressionList callArgs = call.getArgumentList();
if (callArgs != null) {
final PsiExpressionList argumentList = newExpr.getArgumentList();
LOG.assertTrue(argumentList != null);
argumentList.replace(callArgs);
}
call.replace(newExpr);
}
}
}
}
protected MultiMap<PsiElement,String> getConflictDescriptions(final UsageInfo[] usages) {
final MultiMap<PsiElement, String> conflicts = super.getConflictDescriptions(usages);
//Check fields already exist
if (mySettings.isMakeClassParameter()) {
final String fieldName = convertToFieldName(mySettings.getClassParameterName());
final PsiField existing = myMember.findFieldByName(fieldName, false);
if (existing != null) {
String message = RefactoringBundle.message("there.is.already.a.0.in.1", RefactoringUIUtil.getDescription(existing, false),
RefactoringUIUtil.getDescription(myMember, false));
conflicts.putValue(existing, message);
}
}
if (mySettings.isMakeFieldParameters()) {
final List<Settings.FieldParameter> parameterOrderList = mySettings.getParameterOrderList();
for (Settings.FieldParameter parameter : parameterOrderList) {
final String fieldName = convertToFieldName(parameter.name);
final PsiField existing = myMember.findFieldByName(fieldName, false);
if (existing != null) {
String message = RefactoringBundle.message("there.is.already.a.0.in.1", RefactoringUIUtil.getDescription(existing, false),
RefactoringUIUtil.getDescription(myMember, false));
conflicts.putValue(existing, message);
}
}
}
return conflicts;
}
protected void findExternalUsages(final ArrayList<UsageInfo> result) {
PsiMethod[] constructors = myMember.getConstructors();
if (constructors.length > 0) {
for (PsiMethod constructor : constructors) {
findExternalReferences(constructor, result);
}
} else {
findDefaultConstructorReferences(result);
}
}
private void findDefaultConstructorReferences(final ArrayList<UsageInfo> result) {
for (PsiReference ref : ReferencesSearch.search(myMember)) {
PsiElement element = ref.getElement();
if (element.getParent() instanceof PsiNewExpression) {
PsiNewExpression newExpression = (PsiNewExpression)element.getParent();
PsiElement qualifier = newExpression.getQualifier();
if (qualifier instanceof PsiThisExpression) qualifier = null;
if (!PsiTreeUtil.isAncestor(myMember, element, true) || qualifier != null) {
result.add(new UsageInfo(element));
} else {
result.add(new InternalUsageInfo(element, myMember));
}
}
}
}
}