introduce parameter object abstraction

This commit is contained in:
Anna.Kozlova
2016-04-04 21:24:28 +02:00
parent cd53ca0480
commit 555152fa05
6 changed files with 362 additions and 322 deletions
@@ -15,7 +15,6 @@
*/
package com.intellij.refactoring.introduceparameterobject;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,11 +30,10 @@ import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.RefactorJBundle;
import com.intellij.refactoring.changeSignature.ChangeInfo;
@@ -49,11 +47,11 @@ import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
@@ -67,7 +65,6 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
private final PsiMethod method;
private final String className;
private final String packageName;
private final boolean keepMethodAsDelegate;
private final boolean myUseExistingClass;
private final boolean myCreateInnerClass;
private final String myNewVisibility;
@@ -75,11 +72,10 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
private final List<ParameterChunk> parameters;
private final int[] paramsToMerge;
private final List<PsiTypeParameter> typeParams;
private final Set<PsiParameter> paramsNeedingSetters = new HashSet<PsiParameter>();
private final Set<PsiParameter> paramsNeedingGetters = new HashSet<PsiParameter>();
private final PsiClass existingClass;
private PsiMethod myExistingClassCompatibleConstructor;
private ChangeInfo myChangeInfo;
private final String fixedParamName;
public IntroduceParameterObjectProcessor(String className,
String packageName,
@@ -94,7 +90,6 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
this.method = method;
this.className = className;
this.packageName = packageName;
this.keepMethodAsDelegate = keepMethodAsDelegate;
myUseExistingClass = useExistingClass;
myCreateInnerClass = createInnerClass;
myNewVisibility = newVisibility;
@@ -136,6 +131,18 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
final GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
existingClass = JavaPsiFacade.getInstance(myProject).findClass(qualifiedName, scope);
final PsiCodeBlock body = method.getBody();
final String baseParameterName = StringUtil.decapitalize(className);
fixedParamName = body != null
? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true)
: JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER);
myChangeInfo =
new MergeMethodArguments(method, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate,
myCreateInnerClass ? method.getContainingClass() : null).createChangeInfo();
}
@NotNull
@@ -187,34 +194,36 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
public void findUsages(@NotNull List<FixableUsageInfo> usages) {
if (myUseExistingClass && existingClass != null) {
myExistingClassCompatibleConstructor = existingClassIsCompatible(existingClass, parameters);
myExistingClassCompatibleConstructor = JavaIntroduceParameterObjectDelegate.existingClassIsCompatible(existingClass, parameters);
}
final PsiCodeBlock body = method.getBody();
final String baseParameterName = StringUtil.decapitalize(className);
final String fixedParamName =
body != null
? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true)
: JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER);
myChangeInfo =
new MergeMethodArguments(method, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate,
myCreateInnerClass ? method.getContainingClass() : null).createChangeInfo();
for (UsageInfo info : ChangeSignatureProcessorBase.findUsages(myChangeInfo)) {
usages.add(new ChangeSignatureUsageWrapper(info));
}
findUsagesForMethod(method, usages, fixedParamName);
if (myUseExistingClass && existingClass != null && !(paramsNeedingGetters.isEmpty() && paramsNeedingSetters.isEmpty())) {
usages.add(new AppendAccessorsUsageInfo(existingClass, myGenerateAccessors, paramsNeedingGetters, paramsNeedingSetters, parameters));
}
final PsiMethod[] overridingMethods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
for (PsiMethod siblingMethod : overridingMethods) {
findUsagesForMethod(siblingMethod, usages, fixedParamName);
for (int i : paramsToMerge) {
final PsiParameter parameterInBase = method.getParameterList().getParameters()[i];
ParameterChunk parameterChunk = ParameterChunk.getChunkByParameter(parameterInBase, parameters);
assert parameterChunk != null;
@NonNls String getter = parameterChunk.getGetterName(myProject);
@NonNls String setter = parameterChunk.getSetterName(myProject);
final boolean[] needAccessors = {false, false};
findUsagesForMethod(method, usages, fixedParamName, i, needAccessors, getter, setter);
for (PsiMethod siblingMethod : overridingMethods) {
findUsagesForMethod(siblingMethod, usages, fixedParamName, i, needAccessors, getter, setter);
}
final boolean useExisting = myUseExistingClass && existingClass != null;
if (needAccessors[0] && parameterChunk.getGetter() == null) {
usages.add(new AppendAccessorsUsageInfo(existingClass, myGenerateAccessors || !useExisting, parameterInBase, true, parameters));
}
if (needAccessors[1] && parameterChunk.getSetter() == null) {
usages.add(new AppendAccessorsUsageInfo(existingClass, myGenerateAccessors || !useExisting, parameterInBase, false, parameters));
}
}
if (myNewVisibility != null) {
@@ -222,51 +231,40 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
}
private void findUsagesForMethod(PsiMethod overridingMethod, List<FixableUsageInfo> usages, String fixedParamName) {
final ParamUsageVisitor visitor = new ParamUsageVisitor(overridingMethod, paramsToMerge);
overridingMethod.accept(visitor);
final Set<PsiReferenceExpression> values = visitor.getParameterUsages();
for (PsiReferenceExpression paramUsage : values) {
final PsiParameter parameter = (PsiParameter)paramUsage.resolve();
assert parameter != null;
final PsiMethod containingMethod = (PsiMethod)parameter.getDeclarationScope();
final int index = containingMethod.getParameterList().getParameterIndex(parameter);
final PsiParameter replacedParameter = method.getParameterList().getParameters()[index];
final ParameterChunk parameterChunk = ParameterChunk.getChunkByParameter(parameter, parameters);
@NonNls String getter = parameterChunk != null ? parameterChunk.getter : null;
final String paramName = parameterChunk != null ? parameterChunk.parameter.name : replacedParameter.getName();
final PsiType paramType = parameterChunk != null ? parameterChunk.parameter.type : replacedParameter.getType();
if (getter == null) {
getter = parameterChunk != null && parameterChunk.field != null ? GenerateMembersUtil.suggestGetterName(parameterChunk.field)
: GenerateMembersUtil.suggestGetterName(paramName, paramType, myProject);
paramsNeedingGetters.add(replacedParameter);
}
@NonNls String setter = parameterChunk != null ? parameterChunk.setter : null;
if (setter == null) {
setter = parameterChunk != null && parameterChunk.field != null ? GenerateMembersUtil.suggestSetterName(parameterChunk.field)
: GenerateMembersUtil.suggestSetterName(paramName, paramType, myProject);
}
if (RefactoringUtil.isPlusPlusOrMinusMinus(paramUsage.getParent())) {
usages.add(new ReplaceParameterIncrementDecrement(paramUsage, fixedParamName, setter, getter));
if (parameterChunk == null || parameterChunk.setter == null) {
paramsNeedingSetters.add(replacedParameter);
private static void findUsagesForMethod(PsiMethod overridingMethod,
List<FixableUsageInfo> usages,
String fixedParamName,
int i,
final boolean[] needAccessors, String getter, String setter) {
final LocalSearchScope localSearchScope = new LocalSearchScope(overridingMethod);
final PsiParameter[] params = overridingMethod.getParameterList().getParameters();
final PsiParameter parameter = params[i];
ReferencesSearch.search(parameter, localSearchScope).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement refElement = reference.getElement();
if (refElement instanceof PsiReferenceExpression) {
final PsiReferenceExpression paramUsage = (PsiReferenceExpression)refElement;
needAccessors[0] = true;
if (RefactoringUtil.isPlusPlusOrMinusMinus(paramUsage.getParent())) {
usages.add(new ReplaceParameterIncrementDecrement(paramUsage, fixedParamName, setter, getter));
needAccessors[1] = true;
}
else if (RefactoringUtil.isAssignmentLHS(paramUsage)) {
usages.add(new ReplaceParameterAssignmentWithCall(paramUsage, fixedParamName, setter, getter));
needAccessors[1] = true;
}
else {
usages.add(new ReplaceParameterReferenceWithCall(paramUsage, fixedParamName, getter));
}
}
return true;
}
else if (RefactoringUtil.isAssignmentLHS(paramUsage)) {
usages.add(new ReplaceParameterAssignmentWithCall(paramUsage, fixedParamName, setter, getter));
if (parameterChunk == null || parameterChunk.setter == null) {
paramsNeedingSetters.add(replacedParameter);
}
}
else {
usages.add(new ReplaceParameterReferenceWithCall(paramUsage, fixedParamName, getter));
}
}
});
}
protected void performRefactoring(@NotNull UsageInfo[] usageInfos) {
final PsiClass psiClass = buildClass();
final PsiClass psiClass = buildClass(usageInfos);
if (psiClass != null) {
fixJavadocForConstructor(psiClass);
super.performRefactoring(usageInfos);
@@ -289,10 +287,18 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
}
private PsiClass buildClass() {
private PsiClass buildClass(UsageInfo[] usageInfos) {
if (existingClass != null) {
return existingClass;
}
Set<PsiParameter> paramsWithSetters = new HashSet<>();
for (UsageInfo info : usageInfos) {
if (info instanceof AppendAccessorsUsageInfo && !((AppendAccessorsUsageInfo)info).isGetter()) {
paramsWithSetters.add(((AppendAccessorsUsageInfo)info).getParameter());
}
}
final ParameterObjectBuilder beanClassBuilder = new ParameterObjectBuilder();
beanClassBuilder.setVisibility(myCreateInnerClass ? PsiModifier.PRIVATE : PsiModifier.PUBLIC);
beanClassBuilder.setProject(myProject);
@@ -300,8 +306,8 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
beanClassBuilder.setClassName(className);
beanClassBuilder.setPackageName(packageName);
for (ParameterChunk parameterChunk : parameters) {
final VariableData parameter = parameterChunk.parameter;
final boolean setterRequired = paramsNeedingSetters.contains(parameter.variable);
final VariableData parameter = parameterChunk.getParameter();
final boolean setterRequired = paramsWithSetters.contains(parameter.variable);
beanClassBuilder.addField((PsiParameter)parameter.variable, parameter.name, parameter.type, setterRequired);
}
final String classString = beanClassBuilder.buildBeanClass();
@@ -388,181 +394,6 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
private static class ParamUsageVisitor extends JavaRecursiveElementVisitor {
private final Set<PsiParameter> paramsToMerge = new HashSet<PsiParameter>();
private final Set<PsiReferenceExpression> parameterUsages = new HashSet<PsiReferenceExpression>(4);
ParamUsageVisitor(PsiMethod method, int[] paramIndicesToMerge) {
super();
final PsiParameterList paramList = method.getParameterList();
final PsiParameter[] parameters = paramList.getParameters();
for (int i : paramIndicesToMerge) {
paramsToMerge.add(parameters[i]);
}
}
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement referent = expression.resolve();
if (!(referent instanceof PsiParameter)) {
return;
}
final PsiParameter parameter = (PsiParameter)referent;
if (paramsToMerge.contains(parameter)) {
parameterUsages.add(expression);
}
}
public Set<PsiReferenceExpression> getParameterUsages() {
return parameterUsages;
}
}
@Nullable
private static PsiMethod existingClassIsCompatible(PsiClass aClass, List<ParameterChunk> params) {
if (params.size() == 1) {
final ParameterChunk parameterChunk = params.get(0);
final PsiType paramType = parameterChunk.parameter.type;
if (TypeConversionUtil.isPrimitiveWrapper(aClass.getQualifiedName())) {
parameterChunk.setField(aClass.findFieldByName("value", false));
parameterChunk.setGetter(paramType.getCanonicalText() + "Value");
for (PsiMethod constructor : aClass.getConstructors()) {
if (constructorIsCompatible(constructor, params)) return constructor;
}
}
}
final PsiMethod[] constructors = aClass.getConstructors();
PsiMethod compatibleConstructor = null;
for (PsiMethod constructor : constructors) {
if (constructorIsCompatible(constructor, params)) {
compatibleConstructor = constructor;
break;
}
}
if (compatibleConstructor == null) {
return null;
}
final PsiParameterList parameterList = compatibleConstructor.getParameterList();
final PsiParameter[] constructorParams = parameterList.getParameters();
for (int i = 0; i < constructorParams.length; i++) {
final PsiParameter param = constructorParams[i];
final ParameterChunk parameterChunk = params.get(i);
final PsiField field = findFieldAssigned(param, compatibleConstructor);
if (field == null) {
return null;
}
parameterChunk.setField(field);
final PsiMethod getterForField = PropertyUtil.findGetterForField(field);
if (getterForField != null) {
parameterChunk.setGetter(getterForField.getName());
}
final PsiMethod setterForField = PropertyUtil.findSetterForField(field);
if (setterForField != null) {
parameterChunk.setSetter(setterForField.getName());
}
}
return compatibleConstructor;
}
private static boolean constructorIsCompatible(PsiMethod constructor, List<ParameterChunk> params) {
final PsiParameterList parameterList = constructor.getParameterList();
final PsiParameter[] constructorParams = parameterList.getParameters();
if (constructorParams.length != params.size()) {
return false;
}
for (int i = 0; i < constructorParams.length; i++) {
if (!TypeConversionUtil.isAssignable(constructorParams[i].getType(), params.get(i).parameter.type)) {
return false;
}
}
return true;
}
public static class ParameterChunk {
private final VariableData parameter;
private PsiField field;
private String getter;
private String setter;
public ParameterChunk(VariableData parameter) {
this.parameter = parameter;
}
public void setField(PsiField field) {
this.field = field;
}
public void setGetter(String getter) {
this.getter = getter;
}
public void setSetter(String setter) {
this.setter = setter;
}
@Nullable
public PsiField getField() {
return field;
}
@Nullable
public static ParameterChunk getChunkByParameter(PsiParameter param, List<ParameterChunk> params) {
for (ParameterChunk chunk : params) {
if (chunk.parameter.variable.equals(param)) {
return chunk;
}
}
return null;
}
}
private static PsiField findFieldAssigned(PsiParameter param, PsiMethod constructor) {
final ParamAssignmentFinder visitor = new ParamAssignmentFinder(param);
constructor.accept(visitor);
return visitor.getFieldAssigned();
}
private static class ParamAssignmentFinder extends JavaRecursiveElementWalkingVisitor {
private final PsiParameter param;
private PsiField fieldAssigned = null;
ParamAssignmentFinder(PsiParameter param) {
this.param = param;
}
public void visitAssignmentExpression(PsiAssignmentExpression assignment) {
super.visitAssignmentExpression(assignment);
final PsiExpression lhs = assignment.getLExpression();
final PsiExpression rhs = assignment.getRExpression();
if (!(lhs instanceof PsiReferenceExpression)) {
return;
}
if (!(rhs instanceof PsiReferenceExpression)) {
return;
}
final PsiElement referent = ((PsiReference)rhs).resolve();
if (referent == null || !referent.equals(param)) {
return;
}
final PsiElement assigned = ((PsiReference)lhs).resolve();
if (assigned == null || !(assigned instanceof PsiField)) {
return;
}
fieldAssigned = (PsiField)assigned;
}
public PsiField getFieldAssigned() {
return fieldAssigned;
}
}
private static class ChangeSignatureUsageWrapper extends FixableUsageInfo {
private final UsageInfo myInfo;
@@ -0,0 +1,132 @@
/*
* Copyright 2000-2016 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.introduceparameterobject;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class JavaIntroduceParameterObjectDelegate {
@Nullable
static PsiMethod existingClassIsCompatible(PsiClass aClass, List<ParameterChunk> params) {
if (params.size() == 1) {
final ParameterChunk parameterChunk = params.get(0);
final PsiType paramType = parameterChunk.getParameter().type;
if (TypeConversionUtil.isPrimitiveWrapper(aClass.getQualifiedName())) {
parameterChunk.setField(aClass.findFieldByName("value", false));
parameterChunk.setGetter(paramType.getCanonicalText() + "Value");
for (PsiMethod constructor : aClass.getConstructors()) {
if (constructorIsCompatible(constructor, params)) return constructor;
}
}
}
final PsiMethod[] constructors = aClass.getConstructors();
PsiMethod compatibleConstructor = null;
for (PsiMethod constructor : constructors) {
if (constructorIsCompatible(constructor, params)) {
compatibleConstructor = constructor;
break;
}
}
if (compatibleConstructor == null) {
return null;
}
final PsiParameterList parameterList = compatibleConstructor.getParameterList();
final PsiParameter[] constructorParams = parameterList.getParameters();
for (int i = 0; i < constructorParams.length; i++) {
final PsiParameter param = constructorParams[i];
final ParameterChunk parameterChunk = params.get(i);
final PsiField field = findFieldAssigned(param, compatibleConstructor);
if (field == null) {
return null;
}
parameterChunk.setField(field);
final PsiMethod getterForField = PropertyUtil.findGetterForField(field);
if (getterForField != null) {
parameterChunk.setGetter(getterForField.getName());
}
final PsiMethod setterForField = PropertyUtil.findSetterForField(field);
if (setterForField != null) {
parameterChunk.setSetter(setterForField.getName());
}
}
return compatibleConstructor;
}
private static boolean constructorIsCompatible(PsiMethod constructor, List<ParameterChunk> params) {
final PsiParameterList parameterList = constructor.getParameterList();
final PsiParameter[] constructorParams = parameterList.getParameters();
if (constructorParams.length != params.size()) {
return false;
}
for (int i = 0; i < constructorParams.length; i++) {
if (!TypeConversionUtil.isAssignable(constructorParams[i].getType(), params.get(i).getParameter().type)) {
return false;
}
}
return true;
}
private static PsiField findFieldAssigned(PsiParameter param, PsiMethod constructor) {
final ParamAssignmentFinder visitor = new ParamAssignmentFinder(param);
constructor.accept(visitor);
return visitor.getFieldAssigned();
}
private static class ParamAssignmentFinder extends JavaRecursiveElementWalkingVisitor {
private final PsiParameter param;
private PsiField fieldAssigned = null;
ParamAssignmentFinder(PsiParameter param) {
this.param = param;
}
public void visitAssignmentExpression(PsiAssignmentExpression assignment) {
super.visitAssignmentExpression(assignment);
final PsiExpression lhs = assignment.getLExpression();
final PsiExpression rhs = assignment.getRExpression();
if (!(lhs instanceof PsiReferenceExpression)) {
return;
}
if (!(rhs instanceof PsiReferenceExpression)) {
return;
}
final PsiElement referent = ((PsiReference)rhs).resolve();
if (referent == null || !referent.equals(param)) {
return;
}
final PsiElement assigned = ((PsiReference)lhs).resolve();
if (assigned == null || !(assigned instanceof PsiField)) {
return;
}
fieldAssigned = (PsiField)assigned;
}
public PsiField getFieldAssigned() {
return fieldAssigned;
}
}
}
@@ -78,7 +78,8 @@ public class MergeMethodArguments {
packageName = this.packageName;
}
String text = StringUtil.getQualifiedName(packageName, className);
final String qualifiedName = StringUtil.getQualifiedName(packageName, className);
String text = qualifiedName;
if (!typeParams.isEmpty()) {
text += "<" + StringUtil.join(typeParams, new Function<PsiTypeParameter, String>() {
@Override
@@ -94,7 +95,7 @@ public class MergeMethodArguments {
@Override
public PsiExpression getValue(final PsiCallExpression expr) throws IncorrectOperationException {
return (PsiExpression)JavaCodeStyleManager.getInstance(project)
.shortenClassReferences(psiFacade.getElementFactory().createExpressionFromText(getMergedParam(expr), expr));
.shortenClassReferences(psiFacade.getElementFactory().createExpressionFromText(getMergedParam(expr, qualifiedName), expr));
}
};
@@ -130,21 +131,9 @@ public class MergeMethodArguments {
return false;
}
private String getMergedParam(PsiCallExpression call) {
private String getMergedParam(PsiCallExpression call, String qualifiedName) {
final PsiExpression[] args = call.getArgumentList().getExpressions();
StringBuffer newExpression = new StringBuffer();
final String qualifiedName;
if (myContainingClass != null) {
final String containingClassQName = myContainingClass.getQualifiedName();
if (containingClassQName != null) {
qualifiedName = containingClassQName + "." + className;
} else {
qualifiedName = className;
}
}
else {
qualifiedName = StringUtil.getQualifiedName(packageName, className);
}
StringBuilder newExpression = new StringBuilder();
newExpression.append("new ").append(qualifiedName);
if (!typeParams.isEmpty()) {
final JavaResolveResult resolvant = call.resolveMethodGenerics();
@@ -0,0 +1,98 @@
/*
* Copyright 2000-2016 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.introduceparameterobject;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiParameter;
import com.intellij.refactoring.util.VariableData;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ParameterChunk {
private final VariableData parameter;
private PsiField field;
private String getter;
private String setter;
public ParameterChunk(VariableData parameter) {
this.parameter = parameter;
}
public void setField(PsiField field) {
this.field = field;
}
public void setGetter(String getter) {
this.getter = getter;
}
public void setSetter(String setter) {
this.setter = setter;
}
@Nullable
public PsiField getField() {
return field;
}
@Nullable
public static ParameterChunk getChunkByParameter(PsiParameter param, List<ParameterChunk> params) {
for (ParameterChunk chunk : params) {
if (chunk.getParameter().variable.equals(param)) {
return chunk;
}
}
return null;
}
public VariableData getParameter() {
return parameter;
}
public String getGetter() {
return getter;
}
public String getSetter() {
return setter;
}
@NotNull
public String getSetterName(Project project) {
@NonNls String setter = getSetter();
if (setter == null) {
setter = getField() != null ? GenerateMembersUtil.suggestSetterName(getField())
: GenerateMembersUtil.suggestSetterName(parameter.name, parameter.type, project);
}
return setter;
}
@NotNull
public String getGetterName(Project project) {
@NonNls String getter = getGetter();
if (getter == null) {
getter = getField() != null ? GenerateMembersUtil.suggestGetterName(getField())
: GenerateMembersUtil.suggestGetterName(parameter.name, parameter.type, project);
}
return getter;
}
}
@@ -22,91 +22,83 @@ package com.intellij.refactoring.introduceparameterobject.usageInfo;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiParameter;
import com.intellij.refactoring.RefactorJBundle;
import com.intellij.refactoring.introduceparameterobject.IntroduceParameterObjectProcessor;
import com.intellij.refactoring.introduceparameterobject.ParameterChunk;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import java.util.List;
import java.util.Set;
public class AppendAccessorsUsageInfo extends FixableUsageInfo{
private final PsiClass myExistingClass;
private final boolean myGenerateAccessors;
private final Set<PsiParameter> paramsNeedingSetters;
private final Set<PsiParameter> paramsNeedingGetters;
private final List<IntroduceParameterObjectProcessor.ParameterChunk> parameters;
private final PsiParameter myParameter;
private final boolean myGetter;
private final List<ParameterChunk> parameters;
private static final Logger LOGGER = Logger.getInstance("#" + AppendAccessorsUsageInfo.class.getName());
public AppendAccessorsUsageInfo(PsiElement psiClass, boolean generateAccessors, Set<PsiParameter> paramsNeedingGetters,
Set<PsiParameter> paramsNeedingSetters, List<IntroduceParameterObjectProcessor.ParameterChunk> parameters) {
super(psiClass);
public AppendAccessorsUsageInfo(PsiClass existingClass,
boolean generateAccessors,
PsiParameter parameter,
boolean isGetter,
List<ParameterChunk> parameters) {
super(parameter);
myExistingClass = existingClass;
myGenerateAccessors = generateAccessors;
this.paramsNeedingGetters = paramsNeedingGetters;
this.paramsNeedingSetters = paramsNeedingSetters;
myParameter = parameter;
myGetter = isGetter;
this.parameters = parameters;
}
@Override
public void fixUsage() throws IncorrectOperationException {
if (myGenerateAccessors) {
appendAccessors(paramsNeedingGetters, true);
appendAccessors(paramsNeedingSetters, false);
}
}
private void appendAccessors(final Set<PsiParameter> params, boolean isGetter) {
final PsiElement element = getElement();
if (element != null) {
for (PsiParameter parameter : params) {
final IntroduceParameterObjectProcessor.ParameterChunk parameterChunk =
IntroduceParameterObjectProcessor.ParameterChunk.getChunkByParameter(parameter, parameters);
if (myExistingClass != null) {
final ParameterChunk parameterChunk = ParameterChunk.getChunkByParameter(myParameter, parameters);
LOGGER.assertTrue(parameterChunk != null);
final PsiField field = parameterChunk.getField();
if (field != null) {
element.add(isGetter
? GenerateMembersUtil.generateGetterPrototype(field)
: GenerateMembersUtil.generateSetterPrototype(field));
myExistingClass.add(myGetter
? GenerateMembersUtil.generateGetterPrototype(field)
: GenerateMembersUtil.generateSetterPrototype(field));
}
}
}
}
public boolean isGetter() {
return myGetter;
}
public PsiParameter getParameter() {
return myParameter;
}
@Override
public String getConflictMessage() {
if (!myGenerateAccessors && (!paramsNeedingSetters.isEmpty() || !paramsNeedingGetters.isEmpty())) {
final StringBuffer buf = new StringBuffer();
appendConflicts(buf, paramsNeedingGetters);
appendConflicts(buf, paramsNeedingSetters);
return RefactorJBundle.message("cannot.perform.the.refactoring") + buf.toString();
if (!myGenerateAccessors) {
String fieldName = myParameter.getName();
final ParameterChunk chunk = ParameterChunk.getChunkByParameter(myParameter, parameters);
if (chunk != null) {
final PsiField field = chunk.getField();
if (field != null) {
fieldName = field.getName();
}
}
return (myGetter ? "Getter" : "Setter") + " for field \'" + fieldName + "\' is required";
}
return null;
}
private void appendConflicts(StringBuffer buf, final Set<PsiParameter> paramsNeeding) {
if (!paramsNeeding.isEmpty()) {
buf.append(paramsNeeding == paramsNeedingGetters ? "Getters" : "Setters");
buf.append(" for the following fields are required:\n");
buf.append(StringUtil.join(paramsNeeding, new Function<PsiParameter, String>() {
public String fun(PsiParameter psiParameter) {
final IntroduceParameterObjectProcessor.ParameterChunk chunk =
IntroduceParameterObjectProcessor.ParameterChunk.getChunkByParameter(psiParameter, parameters);
if (chunk != null) {
final PsiField field = chunk.getField();
if (field != null) {
return field.getName();
}
}
return psiParameter.getName();
}
}, ", "));
buf.append(".\n");
}
@Override
public boolean equals(Object o) {
return super.equals(o) && ((AppendAccessorsUsageInfo)o).isGetter() == isGetter();
}
@Override
public int hashCode() {
return super.hashCode() * 29 + (isGetter() ? 1 : 0);
}
}
@@ -202,9 +202,7 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{
}
public void testIntegerIncremental() throws Exception {
checkExceptionThrown("Integer", "java.lang", "Cannot perform the refactoring.\n" +
"Setters for the following fields are required:\n" +
"value.\n");
checkExceptionThrown("Integer", "java.lang", "Setter for field 'value' is required");
}
private void checkExceptionThrown(String existingClassName, String existingClassPackage, String exceptionMessage) throws Exception {
@@ -227,7 +225,7 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{
}
public void testExistingBeanIfNoGeneration() throws Exception {
checkExceptionThrown("Param", "", "Cannot perform the refactoring.\n" + "Setters for the following fields are required:\n" + "i.\n");
checkExceptionThrown("Param", "", "Setter for field 'i' is required");
}
public void testParamNameConflict() throws Exception {
@@ -244,6 +242,6 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{
}
public void testWrongBean() throws Exception {
checkExceptionThrown("Param", "", "Cannot perform the refactoring.\n" + "Getters for the following fields are required:\n" + "i.\n");
checkExceptionThrown("Param", "", "Getter for field 'i' is required");
}
}