From 55e24083afe2ba1b5bb501e1b3a5c345ecb7d6e9 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Thu, 7 Apr 2016 18:17:14 +0200 Subject: [PATCH] introduce parameter object: initial --- .../refactoring/RefactorJBundle.properties | 3 - .../changeSignature/OverriderUsageInfo.java | 3 +- .../IntroduceParameterObjectDialog.java | 26 +- .../IntroduceParameterObjectProcessor.java | 412 ------------------ ...uceParameterObjectUsageViewDescriptor.java | 45 -- ...troduceParameterObjectClassDescriptor.java | 403 +++++++++++++++++ .../JavaIntroduceParameterObjectDelegate.java | 310 +++++++++---- .../MergeMethodArguments.java | 185 -------- .../ParameterChunk.java | 98 ----- .../ParameterObjectBuilder.java | 4 +- .../usageInfo/AppendAccessorsUsageInfo.java | 46 +- .../BeanClassVisibilityUsageInfo.java | 12 +- .../ConstructorJavadocUsageInfo.java | 82 ++++ .../IntroduceParameterObjectTest.java | 67 +-- .../OverriderMethodUsageInfo.java | 27 ++ ...troduceParameterObjectClassDescriptor.java | 99 +++++ .../IntroduceParameterObjectDelegate.java | 72 +++ .../IntroduceParameterObjectProcessor.java | 199 +++++++++ ...uceParameterObjectUsageViewDescriptor.java | 45 ++ .../refactoring/util/FixableUsageInfo.java | 0 .../FixableUsagesRefactoringProcessor.java | 6 +- .../src/messages/RefactoringBundle.properties | 5 +- .../src/META-INF/LangExtensionPoints.xml | 5 + resources/src/META-INF/IdeaPlugin.xml | 2 + 24 files changed, 1245 insertions(+), 911 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java delete mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectUsageViewDescriptor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java delete mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java delete mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterChunk.java create mode 100644 java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/ConstructorJavadocUsageInfo.java create mode 100644 platform/lang-api/src/com/intellij/refactoring/changeSignature/OverriderMethodUsageInfo.java create mode 100644 platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectClassDescriptor.java create mode 100644 platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java create mode 100644 platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectProcessor.java create mode 100644 platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectUsageViewDescriptor.java rename {java/java-impl => platform/lang-impl}/src/com/intellij/refactoring/util/FixableUsageInfo.java (100%) rename {java/java-impl => platform/lang-impl}/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java (93%) diff --git a/java/java-impl/src/com/intellij/refactoring/RefactorJBundle.properties b/java/java-impl/src/com/intellij/refactoring/RefactorJBundle.properties index 55482acf4a87..d1de7e3bc4e0 100644 --- a/java/java-impl/src/com/intellij/refactoring/RefactorJBundle.properties +++ b/java/java-impl/src/com/intellij/refactoring/RefactorJBundle.properties @@ -170,9 +170,6 @@ extracted.class.command.name=Extracted class {0} method.to.extract.parameters.from.label=Method to extract parameters from name.for.new.parameter.label=Name for new parameter: method.selected.has.no.parameters=Method selected has no parameters. -introduced.parameter.class.command.name=Introduced parameter class {0} for {1}.{2}() -method.whose.parameters.are.to.wrapped=Method whose parameters are to wrapped -references.to.be.modified=References to be modified parameters.to.extract.border=Parameters to Extract parameter=Parameter your.evaluation.license.for.refactor.j.will.expire.in.less.than.n.days=Your evaluation license for Refactor-J will expire in less than {0} days.\n diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/OverriderUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/OverriderUsageInfo.java index e1e39140c734..cca43e668d9e 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/OverriderUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/OverriderUsageInfo.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; /** * @author ven */ -public class OverriderUsageInfo extends UsageInfo { +public class OverriderUsageInfo extends UsageInfo implements OverriderMethodUsageInfo { private final PsiMethod myBaseMethod; private final boolean myToInsertArgs; private final boolean myToCatchExceptions; @@ -44,6 +44,7 @@ public class OverriderUsageInfo extends UsageInfo { return myBaseMethod; } + @Override public PsiMethod getOverridingMethod() { return myOverridingMethod; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectDialog.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectDialog.java index 5dc305a6884e..d44fe77eb159 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectDialog.java @@ -28,6 +28,9 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.refactoring.*; +import com.intellij.refactoring.changeSignature.JavaMethodDescriptor; +import com.intellij.refactoring.changeSignature.ParameterInfoImpl; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectProcessor; import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox; import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo; import com.intellij.refactoring.ui.RefactoringDialog; @@ -166,20 +169,31 @@ public class IntroduceParameterObjectDialog extends RefactoringDialog { packageName = getPackageName(); className = getClassName(); } - List parameters = new ArrayList(); + final PsiParameterList parameterList = sourceMethod.getParameterList(); + final List parameters = new ArrayList<>(); for (VariableData data : parameterInfo) { if (data.passAsParameter) { - parameters.add(data); + parameters.add(new ParameterInfoImpl(parameterList.getParameterIndex((PsiParameter)data.variable), data.name, data.type)); } } + final ParameterInfoImpl[] infos = parameters.toArray(new ParameterInfoImpl[parameters.size()]); final String newVisibility = myEscalateVisibilityCheckBox.isEnabled() && myEscalateVisibilityCheckBox.isSelected() ? VisibilityUtil.ESCALATE_VISIBILITY : null; final MoveDestination moveDestination = ((DestinationFolderComboBox)myDestinationCb) .selectDirectory(new PackageWrapper(PsiManager.getInstance(myProject), packageName), false); - invokeRefactoring(new IntroduceParameterObjectProcessor(className, packageName, moveDestination, sourceMethod, - parameters.toArray(new VariableData[parameters.size()]), - keepMethod, useExistingClass, - createInnerClass, newVisibility, myGenerateAccessorsCheckBox.isSelected())); + final JavaIntroduceParameterObjectClassDescriptor classDescriptor = + new JavaIntroduceParameterObjectClassDescriptor(className, packageName, moveDestination, useExistingClass, createInnerClass, + newVisibility, infos, sourceMethod, + myGenerateAccessorsCheckBox.isSelected()); + invokeRefactoring( + new IntroduceParameterObjectProcessor(getProject(), + classDescriptor, + sourceMethod, + infos, + new JavaMethodDescriptor( + sourceMethod) + .getParameters(), + keepMethod)); } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java deleted file mode 100644 index 825a752022c2..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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.ide.highlighter.JavaFileType; -import com.intellij.ide.util.PackageUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; -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.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.PsiUtil; -import com.intellij.refactoring.MoveDestination; -import com.intellij.refactoring.RefactorJBundle; -import com.intellij.refactoring.changeSignature.ChangeInfo; -import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; -import com.intellij.refactoring.introduceparameterobject.usageInfo.*; -import com.intellij.refactoring.util.FixableUsageInfo; -import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor; -import com.intellij.refactoring.util.RefactoringUtil; -import com.intellij.refactoring.util.VariableData; -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 java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringProcessor { - private static final Logger logger = Logger.getInstance("com.siyeh.rpp.introduceparameterobject.IntroduceParameterObjectProcessor"); - - private final MoveDestination myMoveDestination; - private final PsiMethod method; - private final String className; - private final String packageName; - private final boolean myUseExistingClass; - private final boolean myCreateInnerClass; - private final String myNewVisibility; - private final boolean myGenerateAccessors; - private final List parameters; - private final int[] paramsToMerge; - private final List typeParams; - private final PsiClass existingClass; - private PsiMethod myExistingClassCompatibleConstructor; - private ChangeInfo myChangeInfo; - private final String fixedParamName; - - public IntroduceParameterObjectProcessor(String className, - String packageName, - MoveDestination moveDestination, - PsiMethod method, - VariableData[] parameters, boolean keepMethodAsDelegate, final boolean useExistingClass, - final boolean createInnerClass, - String newVisibility, - boolean generateAccessors) { - super(method.getProject()); - myMoveDestination = moveDestination; - this.method = method; - this.className = className; - this.packageName = packageName; - myUseExistingClass = useExistingClass; - myCreateInnerClass = createInnerClass; - myNewVisibility = newVisibility; - myGenerateAccessors = generateAccessors; - this.parameters = new ArrayList(); - for (VariableData parameter : parameters) { - this.parameters.add(new ParameterChunk(parameter)); - } - final PsiParameterList parameterList = method.getParameterList(); - final PsiParameter[] methodParams = parameterList.getParameters(); - paramsToMerge = new int[parameters.length]; - for (int p = 0; p < parameters.length; p++) { - VariableData parameter = parameters[p]; - for (int i = 0; i < methodParams.length; i++) { - final PsiParameter methodParam = methodParams[i]; - if (parameter.variable.equals(methodParam)) { - paramsToMerge[p] = i; - break; - } - } - } - final Set typeParamSet = new HashSet(); - final PsiTypeVisitor typeParametersVisitor = new PsiTypeVisitor() { - @Override - public Object visitClassType(PsiClassType classType) { - final PsiClass referent = classType.resolve(); - if (referent instanceof PsiTypeParameter) { - typeParamSet.add((PsiTypeParameter)referent); - } - return super.visitClassType(classType); - } - }; - for (VariableData parameter : parameters) { - parameter.type.accept(typeParametersVisitor); - } - typeParams = new ArrayList(typeParamSet); - - final String qualifiedName = StringUtil.getQualifiedName(packageName, className); - 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 - protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usageInfos) { - return new IntroduceParameterObjectUsageViewDescriptor(method); - } - - - @Override - protected boolean preprocessUsages(@NotNull final Ref refUsages) { - MultiMap conflicts = new MultiMap(); - if (myUseExistingClass) { - if (existingClass == null) { - conflicts.putValue(null, RefactorJBundle.message("cannot.perform.the.refactoring") + "Could not find the selected class"); - } - if (myExistingClassCompatibleConstructor == null) { - conflicts.putValue(existingClass, RefactorJBundle.message("cannot.perform.the.refactoring") + "Selected class has no compatible constructors"); - } - } - else { - if (existingClass != null) { - conflicts.putValue(existingClass, - RefactorJBundle.message("cannot.perform.the.refactoring") + - RefactorJBundle.message("there.already.exists.a.class.with.the.chosen.name")); - } - if (myMoveDestination != null) { - if (!myMoveDestination.isTargetAccessible(myProject, method.getContainingFile().getVirtualFile())) { - conflicts.putValue(method, "Created class won't be accessible"); - } - } - } - List changeSignatureUsages = new ArrayList<>(); - for (UsageInfo usageInfo : refUsages.get()) { - if (usageInfo instanceof FixableUsageInfo) { - final String conflictMessage = ((FixableUsageInfo)usageInfo).getConflictMessage(); - if (conflictMessage != null) { - conflicts.putValue(usageInfo.getElement(), conflictMessage); - } - } - else { - changeSignatureUsages.add(usageInfo); - } - } - - ChangeSignatureProcessorBase.collectConflictsFromExtensions(new Ref<>(changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])), conflicts, myChangeInfo); - - return showConflicts(conflicts, refUsages.get()); - } - - public void findUsages(@NotNull List usages) { - if (myUseExistingClass && existingClass != null) { - myExistingClassCompatibleConstructor = JavaIntroduceParameterObjectDelegate.existingClassIsCompatible(existingClass, parameters); - } - - for (UsageInfo info : ChangeSignatureProcessorBase.findUsages(myChangeInfo)) { - usages.add(new ChangeSignatureUsageWrapper(info)); - } - - final PsiMethod[] overridingMethods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY); - 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) { - usages.add(new BeanClassVisibilityUsageInfo(existingClass, usages.toArray(new UsageInfo[usages.size()]), myNewVisibility, myExistingClassCompatibleConstructor)); - } - } - - private static void findUsagesForMethod(PsiMethod overridingMethod, - List 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() { - @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; - } - }); - } - - protected void performRefactoring(@NotNull UsageInfo[] usageInfos) { - final PsiClass psiClass = buildClass(usageInfos); - if (psiClass != null) { - fixJavadocForConstructor(psiClass); - super.performRefactoring(usageInfos); - if (!myUseExistingClass) { - for (PsiReference reference : ReferencesSearch.search(method)) { - final PsiElement place = reference.getElement(); - VisibilityUtil.escalateVisibility(psiClass, place); - for (PsiMethod constructor : psiClass.getConstructors()) { - VisibilityUtil.escalateVisibility(constructor, place); - } - } - } - List changeSignatureUsages = new ArrayList<>(); - for (UsageInfo info : usageInfos) { - if (info instanceof ChangeSignatureUsageWrapper) { - changeSignatureUsages.add(((ChangeSignatureUsageWrapper)info).getInfo()); - } - } - ChangeSignatureProcessorBase.doChangeSignature(myChangeInfo, changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])); - } - } - - private PsiClass buildClass(UsageInfo[] usageInfos) { - if (existingClass != null) { - return existingClass; - } - - Set 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); - beanClassBuilder.setTypeArguments(typeParams); - beanClassBuilder.setClassName(className); - beanClassBuilder.setPackageName(packageName); - for (ParameterChunk parameterChunk : parameters) { - 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(); - - try { - final PsiFileFactory factory = PsiFileFactory.getInstance(method.getProject()); - final PsiJavaFile newFile = (PsiJavaFile)factory.createFileFromText(className + ".java", JavaFileType.INSTANCE, classString); - if (myCreateInnerClass) { - final PsiClass containingClass = method.getContainingClass(); - final PsiClass[] classes = newFile.getClasses(); - assert classes.length > 0 : classString; - final PsiClass innerClass = (PsiClass)containingClass.add(classes[0]); - PsiUtil.setModifierProperty(innerClass, PsiModifier.STATIC, true); - return (PsiClass)JavaCodeStyleManager.getInstance(newFile.getProject()).shortenClassReferences(innerClass); - } else { - final PsiFile containingFile = method.getContainingFile(); - final PsiDirectory containingDirectory = containingFile.getContainingDirectory(); - final PsiDirectory directory; - if (myMoveDestination != null) { - directory = myMoveDestination.getTargetDirectory(containingDirectory); - } else { - final Module module = ModuleUtil.findModuleForPsiElement(containingFile); - directory = PackageUtil.findOrCreateDirectoryForPackage(module, packageName, containingDirectory, true, true); - } - - if (directory != null) { - - final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(method.getManager().getProject()); - final PsiElement shortenedFile = JavaCodeStyleManager.getInstance(newFile.getProject()).shortenClassReferences(newFile); - final PsiElement reformattedFile = codeStyleManager.reformat(shortenedFile); - return ((PsiJavaFile)directory.add(reformattedFile)).getClasses()[0]; - } - } - } - catch (IncorrectOperationException e) { - logger.info(e); - } - return null; - } - - private void fixJavadocForConstructor(PsiClass psiClass) { - final PsiDocComment docComment = method.getDocComment(); - if (docComment != null) { - final List mergedTags = new ArrayList(); - final PsiDocTag[] paramTags = docComment.findTagsByName("param"); - for (PsiDocTag paramTag : paramTags) { - final PsiElement[] dataElements = paramTag.getDataElements(); - if (dataElements.length > 0) { - if (dataElements[0] instanceof PsiDocParamRef) { - final PsiReference reference = dataElements[0].getReference(); - if (reference != null) { - final PsiElement resolve = reference.resolve(); - if (resolve instanceof PsiParameter) { - final int parameterIndex = method.getParameterList().getParameterIndex((PsiParameter)resolve); - if (ArrayUtil.find(paramsToMerge, parameterIndex) < 0) continue; - } - } - } - mergedTags.add((PsiDocTag)paramTag.copy()); - } - } - - PsiMethod compatibleParamObjectConstructor = null; - if (myExistingClassCompatibleConstructor != null && myExistingClassCompatibleConstructor.getDocComment() == null) { - compatibleParamObjectConstructor = myExistingClassCompatibleConstructor; - } else if (!myUseExistingClass){ - compatibleParamObjectConstructor = psiClass.getConstructors()[0]; - } - - if (compatibleParamObjectConstructor != null) { - PsiDocComment psiDocComment = JavaPsiFacade.getElementFactory(myProject).createDocCommentFromText("/**\n*/"); - psiDocComment = (PsiDocComment)compatibleParamObjectConstructor.addBefore(psiDocComment, compatibleParamObjectConstructor.getFirstChild()); - - for (PsiDocTag tag : mergedTags) { - psiDocComment.add(tag); - } - } - } - } - - protected String getCommandName() { - final PsiClass containingClass = method.getContainingClass(); - return RefactorJBundle.message("introduced.parameter.class.command.name", className, containingClass.getName(), method.getName()); - } - - - private static class ChangeSignatureUsageWrapper extends FixableUsageInfo { - private final UsageInfo myInfo; - - public ChangeSignatureUsageWrapper(UsageInfo info) { - super(info.getElement()); - myInfo = info; - } - - public UsageInfo getInfo() { - return myInfo; - } - - @Override - public void fixUsage() throws IncorrectOperationException {} - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectUsageViewDescriptor.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectUsageViewDescriptor.java deleted file mode 100644 index 87dc870bebe8..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectUsageViewDescriptor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2000-2013 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.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.refactoring.RefactorJBundle; -import com.intellij.refactoring.psi.MyUsageViewUtil; -import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; -import org.jetbrains.annotations.NotNull; - -class IntroduceParameterObjectUsageViewDescriptor extends UsageViewDescriptorAdapter { - - private final PsiMethod method; - - IntroduceParameterObjectUsageViewDescriptor(PsiMethod method) { - - this.method = method; - } - - @NotNull - public PsiElement[] getElements() { - return new PsiElement[]{method}; - } - public String getProcessedElementsHeader() { - return RefactorJBundle.message("method.whose.parameters.are.to.wrapped"); - } - - public String getCodeReferencesText(int usagesCount, int filesCount) { - return RefactorJBundle.message("references.to.be.modified") + MyUsageViewUtil.getUsageCountInfo(usagesCount, filesCount, "reference"); - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java new file mode 100644 index 000000000000..8662f30551a5 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectClassDescriptor.java @@ -0,0 +1,403 @@ +/* + * 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.ide.highlighter.JavaFileType; +import com.intellij.ide.util.PackageUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +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.util.PropertyUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.MoveDestination; +import com.intellij.refactoring.changeSignature.ParameterInfoImpl; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectClassDescriptor; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectDelegate; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class JavaIntroduceParameterObjectClassDescriptor extends IntroduceParameterObjectClassDescriptor { + private static final Logger LOG = Logger.getInstance("#" + JavaIntroduceParameterObjectClassDescriptor.class.getName()); + private final Set myTypeParameters = new HashSet<>(); + private final Map myExistingClassProperties = new HashMap<>(); + private final MoveDestination myMoveDestination; + + private PsiMethod myExistingClassCompatibleConstructor; + + public JavaIntroduceParameterObjectClassDescriptor(String className, + String packageName, + MoveDestination moveDestination, + boolean useExistingClass, + boolean createInnerClass, + String newVisibility, + ParameterInfoImpl[] paramsToMerge, + PsiMethod method, boolean generateAccessors) { + super(className, calcPackageName(packageName, createInnerClass, method), useExistingClass, createInnerClass, + newVisibility, generateAccessors, paramsToMerge); + myMoveDestination = moveDestination; + final PsiTypeVisitor typeParametersVisitor = new PsiTypeVisitor() { + @Override + public Object visitClassType(PsiClassType classType) { + final PsiClass referent = classType.resolve(); + if (referent instanceof PsiTypeParameter) { + myTypeParameters.add((PsiTypeParameter)referent); + } + for (PsiType type : classType.getParameters()) { + type.accept(this); + } + + return super.visitClassType(classType); + } + }; + for (ParameterInfoImpl parameterInfo : paramsToMerge) { + parameterInfo.getTypeWrapper().getType(method, method.getManager()).accept(typeParametersVisitor); + } + } + + private static String calcPackageName(String packageName, boolean createInnerClass, PsiMethod method) { + if (createInnerClass) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null) { + final String qualifiedName = containingClass.getQualifiedName(); + return qualifiedName != null ? qualifiedName : ""; + } + else { + return packageName; + } + } + return packageName; + } + + public Set getTypeParameters() { + return myTypeParameters; + } + + public MoveDestination getMoveDestination() { + return myMoveDestination; + } + + public String createFakeClassTypeText() { + String text = StringUtil.getQualifiedName(getPackageName(), getClassName()); + if (!myTypeParameters.isEmpty()) { + text += "<" + StringUtil.join(myTypeParameters, new Function() { + @Override + public String fun(PsiTypeParameter parameter) { + return parameter.getName(); + } + }, ", ") + ">"; + } + return text; + } + + public PsiMethod getExistingClassCompatibleConstructor() { + return myExistingClassCompatibleConstructor; + } + + public void setExistingClassCompatibleConstructor(PsiMethod existingClassCompatibleConstructor) { + myExistingClassCompatibleConstructor = existingClassCompatibleConstructor; + } + + @Override + public PsiClass getExistingClass() { + return (PsiClass)super.getExistingClass(); + } + + @Override + public void setExistingClass(PsiElement existingClass) { + super.setExistingClass(existingClass); + if (isUseExistingClass()) { + setExistingClassCompatibleConstructor(existingClassIsCompatible((PsiClass)existingClass)); + } + } + + public String getGetter(ParameterInfoImpl param) { + final ParameterBean bean = getBean(param); + return bean != null ? bean.getGetter() : null; + } + + public String getSetter(ParameterInfoImpl param) { + final ParameterBean bean = getBean(param); + return bean != null ? bean.getSetter() : null; + } + + @Override + public String getSetterName(ParameterInfoImpl parameterInfo, PsiElement context) { + final ParameterBean bean = getBean(parameterInfo); + @NonNls String setter = bean != null ? bean.getSetter() : null; + if (setter == null) { + setter = bean != null && bean.getField() != null + ? GenerateMembersUtil.suggestSetterName(bean.getField()) + : GenerateMembersUtil + .suggestSetterName(parameterInfo.getName(), parameterInfo.getTypeWrapper().getType(context, context.getManager()), + context.getProject()); + } + + return setter; + } + + @Override + public String getGetterName(ParameterInfoImpl paramInfo, PsiElement context) { + final ParameterBean bean = getBean(paramInfo); + @NonNls String getter = bean != null ? bean.getGetter() : null; + if (getter == null) { + getter = bean != null && bean.getField() != null ? GenerateMembersUtil.suggestGetterName(bean.getField()) + : GenerateMembersUtil + .suggestGetterName(paramInfo.getName(), paramInfo.getTypeWrapper().getType(context, context.getManager()), + context.getProject()); + } + return getter; + } + + @Override + public void initExistingClass(PsiMethod method) { + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(method.getProject()); + final String qualifiedName = StringUtil.getQualifiedName(getPackageName(), getClassName()); + final PsiClass existingClass = psiFacade.findClass(qualifiedName, method.getResolveScope()); + setExistingClass(existingClass); + } + + @Nullable + public PsiField getField(ParameterInfoImpl parameter) { + final ParameterBean bean = getBean(parameter); + return bean != null ? bean.getField() : null; + } + + private ParameterBean getBean(ParameterInfoImpl param) { + return myExistingClassProperties.get(param); + } + + @Nullable + private PsiMethod existingClassIsCompatible(@NotNull PsiClass aClass) { + ParameterInfoImpl[] paramsToMerge = getParamsToMerge(); + if (paramsToMerge.length == 1) { + final ParameterInfoImpl parameterInfo = paramsToMerge[0]; + final PsiType paramType = parameterInfo.getTypeWrapper().getType(aClass, aClass.getManager()); + if (TypeConversionUtil.isPrimitiveWrapper(aClass.getQualifiedName())) { + ParameterBean bean = new ParameterBean(); + bean.setField(aClass.findFieldByName("value", false)); + bean.setGetter(paramType.getCanonicalText() + "Value"); + myExistingClassProperties.put(parameterInfo, bean); + for (PsiMethod constructor : aClass.getConstructors()) { + if (constructorIsCompatible(constructor, new ParameterInfoImpl[]{parameterInfo}, aClass)) return constructor; + } + } + } + final PsiMethod[] constructors = aClass.getConstructors(); + PsiMethod compatibleConstructor = null; + for (PsiMethod constructor : constructors) { + if (constructorIsCompatible(constructor, paramsToMerge, aClass)) { + 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 ParameterInfoImpl methodParam = getParameterInfo(i); + final ParameterBean bean = new ParameterBean(); + myExistingClassProperties.put(methodParam, bean); + + final PsiField field = findFieldAssigned(param, compatibleConstructor); + if (field == null) { + return null; + } + + bean.setField(field); + + final PsiMethod getterForField = PropertyUtil.findGetterForField(field); + if (getterForField != null) { + bean.setGetter(getterForField.getName()); + } + + final PsiMethod setterForField = PropertyUtil.findSetterForField(field); + if (setterForField != null) { + bean.setSetter(setterForField.getName()); + } + } + return compatibleConstructor; + } + + private boolean constructorIsCompatible(PsiMethod constructor, ParameterInfoImpl[] paramsToMerge, PsiElement context) { + final PsiParameterList parameterList = constructor.getParameterList(); + final PsiParameter[] constructorParams = parameterList.getParameters(); + if (constructorParams.length != paramsToMerge.length) { + return false; + } + for (int i = 0; i < constructorParams.length; i++) { + if (!TypeConversionUtil.isAssignable(constructorParams[i].getType(), + getParameterInfo(i).getTypeWrapper().getType(context, context.getManager()))) { + return false; + } + } + return true; + } + + private static PsiField findFieldAssigned(PsiParameter param, PsiMethod constructor) { + final ParamAssignmentFinder visitor = new ParamAssignmentFinder(param); + constructor.accept(visitor); + return visitor.getFieldAssigned(); + } + + @Override + public PsiClass createClass(PsiMethod method, IntroduceParameterObjectDelegate.Accessor[] accessors) { + if (isUseExistingClass()) { + return getExistingClass(); + } + + final ParameterObjectBuilder beanClassBuilder = new ParameterObjectBuilder(); + beanClassBuilder.setVisibility(isCreateInnerClass() ? PsiModifier.PRIVATE : PsiModifier.PUBLIC); + beanClassBuilder.setProject(method.getProject()); + beanClassBuilder.setTypeArguments(getTypeParameters()); + beanClassBuilder.setClassName(getClassName()); + beanClassBuilder.setPackageName(getPackageName()); + PsiParameter[] parameters = method.getParameterList().getParameters(); + final ParameterInfoImpl[] parameterInfos = getParamsToMerge(); + for (int i = 0; i < parameterInfos.length; i++) { + PsiParameter parameter = parameters[parameterInfos[i].getOldIndex()]; + final boolean setterRequired = accessors[i] == IntroduceParameterObjectDelegate.Accessor.Setter; + final String newName = parameterInfos[i].getName(); + beanClassBuilder + .addField(parameter, newName, parameterInfos[i].getTypeWrapper().getType(method, method.getManager()), setterRequired); + } + + final String classString = beanClassBuilder.buildBeanClass(); + + try { + final PsiFileFactory factory = PsiFileFactory.getInstance(method.getProject()); + final PsiJavaFile newFile = + (PsiJavaFile)factory.createFileFromText(getClassName() + ".java", JavaFileType.INSTANCE, classString); + if (isCreateInnerClass()) { + final PsiClass containingClass = method.getContainingClass(); + final PsiClass[] classes = newFile.getClasses(); + assert classes.length > 0 : classString; + final PsiClass innerClass = (PsiClass)containingClass.add(classes[0]); + PsiUtil.setModifierProperty(innerClass, PsiModifier.STATIC, true); + return (PsiClass)JavaCodeStyleManager.getInstance(newFile.getProject()).shortenClassReferences(innerClass); + } + else { + final PsiFile containingFile = method.getContainingFile(); + final PsiDirectory containingDirectory = containingFile.getContainingDirectory(); + final PsiDirectory directory; + final MoveDestination moveDestination = getMoveDestination(); + if (moveDestination != null) { + directory = moveDestination.getTargetDirectory(containingDirectory); + } + else { + final Module module = ModuleUtilCore.findModuleForPsiElement(containingFile); + directory = + PackageUtil.findOrCreateDirectoryForPackage(module, getPackageName(), containingDirectory, true, true); + } + + if (directory != null) { + + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(method.getManager().getProject()); + final PsiElement shortenedFile = JavaCodeStyleManager.getInstance(newFile.getProject()).shortenClassReferences(newFile); + final PsiElement reformattedFile = codeStyleManager.reformat(shortenedFile); + return ((PsiJavaFile)directory.add(reformattedFile)).getClasses()[0]; + } + } + } + catch (IncorrectOperationException e) { + LOG.info(e); + } + return null; + } + + + 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 ParameterBean { + private PsiField myField; + private String myGetter; + private String mySetter; + + public PsiField getField() { + return myField; + } + + public void setField(PsiField field) { + myField = field; + } + + public String getGetter() { + return myGetter; + } + + public void setGetter(String getter) { + myGetter = getter; + } + + public String getSetter() { + return mySetter; + } + + public void setSetter(String setter) { + mySetter = setter; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java index ee5cecaa827a..3e4a8cdb4757 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/JavaIntroduceParameterObjectDelegate.java @@ -15,118 +15,242 @@ */ package com.intellij.refactoring.introduceparameterobject; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; -import com.intellij.psi.util.PropertyUtil; -import com.intellij.psi.util.TypeConversionUtil; +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.refactoring.MoveDestination; +import com.intellij.refactoring.changeSignature.*; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectClassDescriptor; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectDelegate; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectProcessor; +import com.intellij.refactoring.introduceparameterobject.usageInfo.*; +import com.intellij.refactoring.util.CanonicalTypes; +import com.intellij.refactoring.util.FixableUsageInfo; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.usageView.UsageInfo; +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.Nullable; +import java.util.Collection; +import java.util.Collections; import java.util.List; -public class JavaIntroduceParameterObjectDelegate { +public class JavaIntroduceParameterObjectDelegate + extends IntroduceParameterObjectDelegate { + + @Override + public ParameterInfoImpl createMergedParameterInfo(Project project, + JavaIntroduceParameterObjectClassDescriptor descriptor, + int[] paramsToMerge, + PsiMethod method) { + final PsiCodeBlock body = method.getBody(); + final String baseParameterName = StringUtil.decapitalize(descriptor.getClassName()); + + final String paramName = body != null + ? JavaCodeStyleManager.getInstance(project) + .suggestUniqueVariableName(baseParameterName, body.getLBrace(), true) + : JavaCodeStyleManager.getInstance(project) + .propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER); + + final boolean lastVarargsToMerge = + method.isVarArgs() && ArrayUtil.find(paramsToMerge, method.getParameterList().getParametersCount() - 1) > -1; + final String classTypeText = descriptor.createFakeClassTypeText(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + return new ParameterInfoImpl(-1, paramName, facade.getElementFactory().createTypeFromText(classTypeText, method), null) { + @Override + public PsiExpression getValue(final PsiCallExpression expr) throws IncorrectOperationException { + final String qualifiedName = StringUtil.getQualifiedName(descriptor.getPackageName(), descriptor.getClassName()); + final PsiClass existingClass = facade.findClass(qualifiedName, expr.getResolveScope()); + if (existingClass == null) return null; + final String mergedParam = getMergedParam(expr, existingClass, paramsToMerge, method, lastVarargsToMerge); + return (PsiExpression)JavaCodeStyleManager.getInstance(project) + .shortenClassReferences(facade.getElementFactory().createExpressionFromText(mergedParam, expr)); + } + }; + } + + private static String getMergedParam(PsiCallExpression call, + PsiClass existingClass, + int[] paramsToMerge, + PsiMethod method, + boolean lastVarargsToMerge) { + final PsiExpression[] args = call.getArgumentList().getExpressions(); + StringBuilder newExpression = new StringBuilder(); + final JavaResolveResult resolvant = call.resolveMethodGenerics(); + final PsiSubstitutor substitutor = resolvant.getSubstitutor(); + newExpression.append("new ") + .append(JavaPsiFacade.getElementFactory(call.getProject()).createType(existingClass, substitutor).getCanonicalText()); + newExpression.append('('); + boolean isFirst = true; + for (int index : paramsToMerge) { + if (!isFirst) { + newExpression.append(", "); + } + isFirst = false; + newExpression.append(getArgument(args, index, method)); + } + if (lastVarargsToMerge) { + final int lastArg = paramsToMerge[paramsToMerge.length - 1]; + for (int i = lastArg + 1; i < args.length; i++) { + newExpression.append(','); + newExpression.append(getArgument(args, i, method)); + } + } + newExpression.append(')'); + return newExpression.toString(); + } @Nullable - static PsiMethod existingClassIsCompatible(PsiClass aClass, List 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; + private static String getArgument(PsiExpression[] args, int i, PsiMethod method) { + if (i < args.length) { + return args[i].getText(); + } + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (i < parameters.length) return parameters[i].getName(); + return null; + } + + @Override + public ChangeInfo createChangeSignatureInfo(PsiMethod method, List infos, boolean delegate) { + PsiType returnType = method.getReturnType(); + return new JavaChangeInfoImpl(VisibilityUtil.getVisibilityModifier(method.getModifierList()), + method, + method.getName(), + returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null, + infos.toArray(new ParameterInfoImpl[infos.size()]), + null, + delegate, + Collections.emptySet(), + Collections.emptySet()); + } + + @Override + public Accessor collectInternalUsages(Collection usages, + PsiMethod overridingMethod, + M1 element, + IntroduceParameterObjectClassDescriptor classDescriptor, + int parameterIdx, + String mergedParamName) { + final LocalSearchScope localSearchScope = new LocalSearchScope(overridingMethod); + final PsiParameter[] params = overridingMethod.getParameterList().getParameters(); + final PsiParameter parameter = params[parameterIdx]; + final P1 parameterInfo = classDescriptor.getParameterInfo(parameterIdx); + final String setter = classDescriptor.getSetterName(parameterInfo, overridingMethod); + final String getter = classDescriptor.getGetterName(parameterInfo, overridingMethod); + final Accessor[] accessor = new Accessor[]{null}; + ReferencesSearch.search(parameter, localSearchScope).forEach(new Processor() { + @Override + public boolean process(PsiReference reference) { + final PsiElement refElement = reference.getElement(); + if (refElement instanceof PsiReferenceExpression) { + final PsiReferenceExpression paramUsage = (PsiReferenceExpression)refElement; + if (RefactoringUtil.isPlusPlusOrMinusMinus(paramUsage.getParent())) { + accessor[0] = Accessor.Setter; + usages.add(new ReplaceParameterIncrementDecrement(paramUsage, mergedParamName, setter, getter)); + } + else if (RefactoringUtil.isAssignmentLHS(paramUsage)) { + accessor[0] = Accessor.Setter; + usages.add(new ReplaceParameterAssignmentWithCall(paramUsage, mergedParamName, setter, getter)); + } + else { + if (accessor[0] == null) { + accessor[0] = Accessor.Getter; + } + usages.add(new ReplaceParameterReferenceWithCall(paramUsage, mergedParamName, getter)); + } + } + return true; + } + }); + return accessor[0]; + } + + @Override + public void collectAccessibilityUsages(Collection usages, + PsiMethod method, + JavaIntroduceParameterObjectClassDescriptor descriptor, + Accessor[] accessors) { + final ParameterInfoImpl[] parameterInfos = descriptor.getParamsToMerge(); + final PsiClass existingClass = descriptor.getExistingClass(); + final boolean useExisting = descriptor.isGenerateAccessors() || !(descriptor.isUseExistingClass() && existingClass != null); + + final PsiParameter[] psiParameters = method.getParameterList().getParameters(); + for (int i = 0; i < parameterInfos.length; i++) { + int oldParamIdx = parameterInfos[i].getOldIndex(); + final IntroduceParameterObjectDelegate.Accessor accessor = accessors[i]; + if (accessor != null) { + final ParameterInfoImpl parameterInfo = parameterInfos[i]; + final PsiParameter parameter = psiParameters[oldParamIdx]; + final PsiField field = descriptor.getField(parameterInfo); + final String getter = descriptor.getGetter(parameterInfo); + if (getter == null) { + usages.add(new AppendAccessorsUsageInfo(parameter, existingClass, useExisting, parameterInfo, true, field)); + } + + if (accessor == IntroduceParameterObjectDelegate.Accessor.Setter && descriptor.getSetter(parameterInfo) == null) { + usages.add(new AppendAccessorsUsageInfo(parameter, existingClass, useExisting, parameterInfo, false, field)); } } } - final PsiMethod[] constructors = aClass.getConstructors(); - PsiMethod compatibleConstructor = null; - for (PsiMethod constructor : constructors) { - if (constructorIsCompatible(constructor, params)) { - compatibleConstructor = constructor; - break; - } + + + final String newVisibility = descriptor.getNewVisibility(); + if (newVisibility != null) { + usages.add(new BeanClassVisibilityUsageInfo(existingClass, usages.toArray(UsageInfo.EMPTY_ARRAY), newVisibility, descriptor)); } - if (compatibleConstructor == null) { - return null; + + usages.add(new ConstructorJavadocUsageInfo(method, descriptor)); + + if (!descriptor.isUseExistingClass()) { + usages.add(new FixableUsageInfo(method) { + @Override + public void fixUsage() throws IncorrectOperationException { + final PsiClass psiClass = descriptor.getExistingClass(); + for (PsiReference reference : ReferencesSearch.search(method)) { + final PsiElement place = reference.getElement(); + VisibilityUtil.escalateVisibility(psiClass, place); + for (PsiMethod constructor : psiClass.getConstructors()) { + VisibilityUtil.escalateVisibility(constructor, place); + } + } + } + }); } - 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 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; + @Override + public void collectConflicts(MultiMap conflicts, + UsageInfo[] infos, + PsiMethod method, + JavaIntroduceParameterObjectClassDescriptor classDescriptor) { + final MoveDestination moveDestination = classDescriptor.getMoveDestination(); + if (moveDestination != null) { + if (!moveDestination.isTargetAccessible(method.getProject(), method.getContainingFile().getVirtualFile())) { + conflicts.putValue(method, "Created class won't be accessible"); } } - return true; - } - private static PsiField findFieldAssigned(PsiParameter param, PsiMethod constructor) { - final ParamAssignmentFinder visitor = new ParamAssignmentFinder(param); - constructor.accept(visitor); - return visitor.getFieldAssigned(); - } + if (moveDestination != null) { + for (UsageInfo info : infos) { + if (info instanceof IntroduceParameterObjectProcessor.ChangeSignatureUsageWrapper) { + final UsageInfo usageInfo = ((IntroduceParameterObjectProcessor.ChangeSignatureUsageWrapper)info).getInfo(); + if (usageInfo instanceof OverriderMethodUsageInfo) { + final PsiElement overridingMethod = ((OverriderMethodUsageInfo)usageInfo).getOverridingMethod(); - 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 (!moveDestination.isTargetAccessible(overridingMethod.getProject(), overridingMethod.getContainingFile().getVirtualFile())) { + conflicts.putValue(overridingMethod, "Created class won't be accessible"); + } + } + } } - 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; } } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java deleted file mode 100644 index 8f9a93c204d1..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.refactoring.changeSignature.ChangeInfo; -import com.intellij.refactoring.changeSignature.JavaChangeInfoImpl; -import com.intellij.refactoring.changeSignature.ParameterInfoImpl; -import com.intellij.refactoring.util.CanonicalTypes; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.VisibilityUtil; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class MergeMethodArguments { - private final PsiMethod method; - private final PsiClass myContainingClass; - private final boolean myKeepMethodAsDelegate; - private final List typeParams; - private final String className; - private final String packageName; - private final String parameterName; - private final int[] paramsToMerge; - private final boolean lastParamIsVararg; - - public MergeMethodArguments(PsiMethod method, - String className, - String packageName, - String parameterName, - int[] paramsToMerge, - List typeParams, - final boolean keepMethodAsDelegate, - final PsiClass containingClass) { - this.paramsToMerge = paramsToMerge; - this.packageName = packageName; - this.className = className; - this.parameterName = parameterName; - this.method = method; - myContainingClass = containingClass; - lastParamIsVararg = method.isVarArgs() && isParameterToMerge(method.getParameterList().getParametersCount() - 1); - myKeepMethodAsDelegate = keepMethodAsDelegate; - this.typeParams = new ArrayList(typeParams); - } - - public ChangeInfo createChangeInfo() { - final Project project = method.getProject(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - String packageName; - if (myContainingClass != null) { - packageName = myContainingClass.getQualifiedName(); - if (packageName == null) { - packageName = myContainingClass.getName(); - } - } - else { - packageName = this.packageName; - } - - final String qualifiedName = StringUtil.getQualifiedName(packageName, className); - String text = qualifiedName; - if (!typeParams.isEmpty()) { - text += "<" + StringUtil.join(typeParams, new Function() { - @Override - public String fun(PsiTypeParameter parameter) { - return parameter.getName(); - } - }, ", ") + ">"; - } - final PsiType classType = factory.createTypeFromText(text, method); - final List parametersInfo = new ArrayList(); - - final ParameterInfoImpl mergedParamInfo = new ParameterInfoImpl(-1, parameterName, classType, null) { - @Override - public PsiExpression getValue(final PsiCallExpression expr) throws IncorrectOperationException { - return (PsiExpression)JavaCodeStyleManager.getInstance(project) - .shortenClassReferences(psiFacade.getElementFactory().createExpressionFromText(getMergedParam(expr, qualifiedName), expr)); - } - }; - - int firstIncludedIdx = -1; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - for (int i = 0; i < parameters.length; i++) { - if (!isParameterToMerge(i)) { - parametersInfo.add(new ParameterInfoImpl(i, parameters[i].getName(), parameters[i].getType())); - } else if (firstIncludedIdx == -1) { - firstIncludedIdx = i; - } - } - - parametersInfo.add(firstIncludedIdx == -1 ? 0 : firstIncludedIdx, mergedParamInfo); - PsiType returnType = method.getReturnType(); - return new JavaChangeInfoImpl(VisibilityUtil.getVisibilityModifier(method.getModifierList()), - method, - method.getName(), - returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null, - parametersInfo.toArray(new ParameterInfoImpl[parametersInfo.size()]), - null, - myKeepMethodAsDelegate, - Collections.emptySet(), - Collections.emptySet()); - } - - private boolean isParameterToMerge(int index) { - for (int i : paramsToMerge) { - if (i == index) { - return true; - } - } - return false; - } - - private String getMergedParam(PsiCallExpression call, String qualifiedName) { - final PsiExpression[] args = call.getArgumentList().getExpressions(); - StringBuilder newExpression = new StringBuilder(); - newExpression.append("new ").append(qualifiedName); - if (!typeParams.isEmpty()) { - final JavaResolveResult resolvant = call.resolveMethodGenerics(); - final PsiSubstitutor substitutor = resolvant.getSubstitutor(); - newExpression.append('<'); - final Map substitutionMap = substitutor.getSubstitutionMap(); - newExpression.append(StringUtil.join(typeParams, new Function() { - public String fun(final PsiTypeParameter typeParameter) { - final PsiType boundType = substitutionMap.get(typeParameter); - if (boundType != null) { - return boundType.getCanonicalText(); - } - else { - return typeParameter.getName(); - } - } - }, ", ")); - newExpression.append('>'); - } - newExpression.append('('); - boolean isFirst = true; - for (int index : paramsToMerge) { - if (!isFirst) { - newExpression.append(", "); - } - isFirst = false; - newExpression.append(getArgument(args, index)); - } - if (lastParamIsVararg) { - final int lastArg = paramsToMerge[paramsToMerge.length - 1]; - for (int i = lastArg + 1; i < args.length; i++) { - newExpression.append(','); - newExpression.append(getArgument(args, i)); - } - } - newExpression.append(')'); - return newExpression.toString(); - } - - @Nullable - private String getArgument(PsiExpression[] args, int i) { - if (i < args.length) { - return args[i].getText(); - } - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (i < parameters.length) return parameters[i].getName(); - return null; - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterChunk.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterChunk.java deleted file mode 100644 index 1b3dd41a04ad..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterChunk.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 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; - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java index edf33d5b1e82..ae0fc205a68c 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/ParameterObjectBuilder.java @@ -17,7 +17,6 @@ package com.intellij.refactoring.introduceparameterobject; import com.intellij.codeInsight.generation.GenerateMembersUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -26,6 +25,7 @@ import com.intellij.psi.javadoc.PsiDocComment; import org.jetbrains.annotations.NonNls; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -53,7 +53,7 @@ class ParameterObjectBuilder { fields.add(field); } - public void setTypeArguments(List typeParams) { + public void setTypeArguments(Collection typeParams) { this.typeParams.clear(); this.typeParams.addAll(typeParams); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/AppendAccessorsUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/AppendAccessorsUsageInfo.java index 7d3ec5be5d15..7d961d7901f0 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/AppendAccessorsUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/AppendAccessorsUsageInfo.java @@ -21,50 +21,40 @@ package com.intellij.refactoring.introduceparameterobject.usageInfo; import com.intellij.codeInsight.generation.GenerateMembersUtil; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiField; import com.intellij.psi.PsiParameter; -import com.intellij.refactoring.introduceparameterobject.ParameterChunk; +import com.intellij.refactoring.changeSignature.ParameterInfoImpl; import com.intellij.refactoring.util.FixableUsageInfo; import com.intellij.util.IncorrectOperationException; -import java.util.List; - public class AppendAccessorsUsageInfo extends FixableUsageInfo{ private final PsiClass myExistingClass; private final boolean myGenerateAccessors; - private final PsiParameter myParameter; + private final ParameterInfoImpl myParameter; private final boolean myGetter; - private final List parameters; - private static final Logger LOGGER = Logger.getInstance("#" + AppendAccessorsUsageInfo.class.getName()); + private final PsiField myField; - public AppendAccessorsUsageInfo(PsiClass existingClass, + public AppendAccessorsUsageInfo(PsiParameter psiParameter, + PsiClass existingClass, boolean generateAccessors, - PsiParameter parameter, + ParameterInfoImpl parameter, boolean isGetter, - List parameters) { - super(parameter); + PsiField field) { + super(psiParameter); myExistingClass = existingClass; myGenerateAccessors = generateAccessors; myParameter = parameter; myGetter = isGetter; - this.parameters = parameters; + myField = field; } @Override public void fixUsage() throws IncorrectOperationException { - if (myGenerateAccessors) { - if (myExistingClass != null) { - final ParameterChunk parameterChunk = ParameterChunk.getChunkByParameter(myParameter, parameters); - LOGGER.assertTrue(parameterChunk != null); - final PsiField field = parameterChunk.getField(); - if (field != null) { - myExistingClass.add(myGetter - ? GenerateMembersUtil.generateGetterPrototype(field) - : GenerateMembersUtil.generateSetterPrototype(field)); - } - } + if (myGenerateAccessors && myField != null) { + myExistingClass.add(myGetter + ? GenerateMembersUtil.generateGetterPrototype(myField) + : GenerateMembersUtil.generateSetterPrototype(myField)); } } @@ -72,7 +62,7 @@ public class AppendAccessorsUsageInfo extends FixableUsageInfo{ return myGetter; } - public PsiParameter getParameter() { + public ParameterInfoImpl getParameter() { return myParameter; } @@ -80,12 +70,8 @@ public class AppendAccessorsUsageInfo extends FixableUsageInfo{ public String getConflictMessage() { 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(); - } + if (myField != null) { + fieldName = myField.getName(); } return (myGetter ? "Getter" : "Setter") + " for field \'" + fieldName + "\' is required"; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/BeanClassVisibilityUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/BeanClassVisibilityUsageInfo.java index fab0644eeb22..51d1f2069f5f 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/BeanClassVisibilityUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/BeanClassVisibilityUsageInfo.java @@ -22,6 +22,7 @@ package com.intellij.refactoring.introduceparameterobject.usageInfo; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; +import com.intellij.refactoring.introduceparameterobject.JavaIntroduceParameterObjectClassDescriptor; import com.intellij.refactoring.util.FixableUsageInfo; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewUtil; @@ -32,24 +33,25 @@ public class BeanClassVisibilityUsageInfo extends FixableUsageInfo { private final PsiClass existingClass; private final UsageInfo[] usages; private final String myNewVisibility; - private final PsiMethod myExistingClassCompatibleConstructor; + private final JavaIntroduceParameterObjectClassDescriptor myClassDescriptor; public BeanClassVisibilityUsageInfo(PsiClass existingClass, UsageInfo[] usages, String newVisibility, - PsiMethod existingClassCompatibleConstructor) { + JavaIntroduceParameterObjectClassDescriptor classDescriptor) { super(existingClass); this.existingClass = existingClass; this.usages = usages; myNewVisibility = newVisibility; - myExistingClassCompatibleConstructor = existingClassCompatibleConstructor; + myClassDescriptor = classDescriptor; } @Override public void fixUsage() throws IncorrectOperationException { VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usages), existingClass, myNewVisibility); - if (myExistingClassCompatibleConstructor != null) { - VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usages), myExistingClassCompatibleConstructor, myNewVisibility); + final PsiMethod compatibleConstructor = myClassDescriptor.getExistingClassCompatibleConstructor(); + if (compatibleConstructor != null) { + VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usages), compatibleConstructor, myNewVisibility); } } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/ConstructorJavadocUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/ConstructorJavadocUsageInfo.java new file mode 100644 index 000000000000..447b8c5d6bb2 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/ConstructorJavadocUsageInfo.java @@ -0,0 +1,82 @@ +/* + * 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.usageInfo; + +import com.intellij.psi.*; +import com.intellij.psi.impl.source.javadoc.PsiDocParamRef; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.refactoring.introduceparameterobject.JavaIntroduceParameterObjectClassDescriptor; +import com.intellij.refactoring.util.FixableUsageInfo; +import com.intellij.util.IncorrectOperationException; + +import java.util.ArrayList; +import java.util.List; + +public class ConstructorJavadocUsageInfo extends FixableUsageInfo { + private final PsiMethod myMethod; + private final JavaIntroduceParameterObjectClassDescriptor myDescriptor; + + public ConstructorJavadocUsageInfo(PsiMethod method, JavaIntroduceParameterObjectClassDescriptor descriptor) { + super(method); + myMethod = method; + myDescriptor = descriptor; + } + + @Override + public void fixUsage() throws IncorrectOperationException { + final PsiDocComment docComment = myMethod.getDocComment(); + if (docComment != null) { + final List mergedTags = new ArrayList(); + final PsiDocTag[] paramTags = docComment.findTagsByName("param"); + for (PsiDocTag paramTag : paramTags) { + final PsiElement[] dataElements = paramTag.getDataElements(); + if (dataElements.length > 0) { + if (dataElements[0] instanceof PsiDocParamRef) { + final PsiReference reference = dataElements[0].getReference(); + if (reference != null) { + final PsiElement resolve = reference.resolve(); + if (resolve instanceof PsiParameter) { + final int parameterIndex = myMethod.getParameterList().getParameterIndex((PsiParameter)resolve); + if (myDescriptor.getParameterInfo(parameterIndex) == null) continue; + } + } + } + mergedTags.add((PsiDocTag)paramTag.copy()); + } + } + + PsiMethod compatibleParamObjectConstructor = null; + final PsiMethod existingConstructor = myDescriptor.getExistingClassCompatibleConstructor(); + if (existingConstructor != null && existingConstructor.getDocComment() == null) { + compatibleParamObjectConstructor = existingConstructor; + } + else if (!myDescriptor.isUseExistingClass()) { + compatibleParamObjectConstructor = myDescriptor.getExistingClass().getConstructors()[0]; + } + + if (compatibleParamObjectConstructor != null) { + PsiDocComment psiDocComment = JavaPsiFacade.getElementFactory(myMethod.getProject()).createDocCommentFromText("/**\n*/"); + psiDocComment = + (PsiDocComment)compatibleParamObjectConstructor.addBefore(psiDocComment, compatibleParamObjectConstructor.getFirstChild()); + + for (PsiDocTag tag : mergedTags) { + psiDocComment.add(tag); + } + } + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java index c822fd62d682..cb7b150a69e9 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterObjectTest.java @@ -25,12 +25,16 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.refactoring.introduceparameterobject.IntroduceParameterObjectProcessor; -import com.intellij.refactoring.util.VariableData; +import com.intellij.refactoring.changeSignature.JavaMethodDescriptor; +import com.intellij.refactoring.changeSignature.ParameterInfoImpl; +import com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectProcessor; +import com.intellij.refactoring.introduceparameterobject.JavaIntroduceParameterObjectClassDescriptor; import com.intellij.util.Function; import com.intellij.util.VisibilityUtil; import org.jetbrains.annotations.NotNull; +import java.util.List; + public class IntroduceParameterObjectTest extends MultiFileTestCase{ @NotNull @Override @@ -52,30 +56,36 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ private void doTest(final boolean delegate, final boolean createInner, - final Function function) throws Exception { + final Function function) throws Exception { doTest((rootDir, rootAfter) -> { PsiClass aClass = myJavaFacade.findClass("Test", GlobalSearchScope.projectScope(getProject())); assertNotNull("Class Test not found", aClass); final PsiMethod method = aClass.findMethodsByName("foo", false)[0]; - final VariableData[] datas = function.fun(method); + final ParameterInfoImpl[] datas = function.fun(method); - IntroduceParameterObjectProcessor processor = new IntroduceParameterObjectProcessor("Param", "", null, method, datas, delegate, false, - createInner, null, false); + final JavaIntroduceParameterObjectClassDescriptor classDescriptor = + new JavaIntroduceParameterObjectClassDescriptor("Param", "", null, false, createInner, null, datas, method, false); + final List parameters = new JavaMethodDescriptor(method).getParameters(); + IntroduceParameterObjectProcessor processor = + new IntroduceParameterObjectProcessor(getProject(), + classDescriptor, + method, + datas, + parameters, + delegate); processor.run(); }); } - private static VariableData[] generateParams(final PsiMethod method) { + private static ParameterInfoImpl[] generateParams(final PsiMethod method) { final PsiParameter[] parameters = method.getParameterList().getParameters(); - final VariableData[] datas = new VariableData[parameters.length]; + final ParameterInfoImpl[] datas = new ParameterInfoImpl[parameters.length]; for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; - datas[i] = new VariableData(parameter); - datas[i].name = parameter.getName(); - datas[i].passAsParameter = true; + datas[i] = new ParameterInfoImpl(i, parameter.getName(), parameter.getType()); } return datas; } @@ -128,12 +138,10 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ doTest(false, false, method -> { final PsiParameter[] parameters = method.getParameterList().getParameters(); - final VariableData[] datas = new VariableData[parameters.length - 1]; + final ParameterInfoImpl[] datas = new ParameterInfoImpl[parameters.length - 1]; for (int i = 0; i < parameters.length - 1; i++) { PsiParameter parameter = parameters[i]; - datas[i] = new VariableData(parameter); - datas[i].name = parameter.getName(); - datas[i].passAsParameter = true; + datas[i] = new ParameterInfoImpl(i, parameter.getName(), parameter.getType()); } return datas; }); @@ -143,12 +151,10 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ doTest(false, true, method -> { final PsiParameter[] parameters = method.getParameterList().getParameters(); - final VariableData[] datas = new VariableData[parameters.length - 1]; + final ParameterInfoImpl[] datas = new ParameterInfoImpl[parameters.length - 1]; for (int i = 0; i < parameters.length - 1; i++) { PsiParameter parameter = parameters[i]; - datas[i] = new VariableData(parameter); - datas[i].name = parameter.getName(); - datas[i].passAsParameter = true; + datas[i] = new ParameterInfoImpl(i, parameter.getName(), parameter.getType()); } return datas; }); @@ -158,11 +164,9 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ doTest(false, true, psiMethod -> { final PsiParameter parameter = psiMethod.getParameterList().getParameters()[0]; final PsiClass collectionClass = getJavaFacade().findClass(CommonClassNames.JAVA_UTIL_COLLECTION); - final VariableData variableData = - new VariableData(parameter, JavaPsiFacade.getElementFactory(getProject()).createType(collectionClass)); - variableData.name = parameter.getName(); - variableData.passAsParameter = true; - return new VariableData[]{variableData}; + final ParameterInfoImpl variableData = + new ParameterInfoImpl(0, parameter.getName(), JavaPsiFacade.getElementFactory(getProject()).createType(collectionClass)); + return new ParameterInfoImpl[]{variableData}; }); } @@ -188,9 +192,18 @@ public class IntroduceParameterObjectTest extends MultiFileTestCase{ assertNotNull("Class Test not found", aClass); final PsiMethod method = aClass.findMethodsByName("foo", false)[0]; - IntroduceParameterObjectProcessor processor = new IntroduceParameterObjectProcessor(existingClassName, existingClassPackage, null, method, - generateParams(method), false, true, - false, newVisibility, generateAccessors); + final ParameterInfoImpl[] mergedParams = generateParams(method); + final JavaIntroduceParameterObjectClassDescriptor classDescriptor = + new JavaIntroduceParameterObjectClassDescriptor(existingClassName, existingClassPackage, null, true, false, newVisibility, + mergedParams, method, generateAccessors); + final List parameters = new JavaMethodDescriptor(method).getParameters(); + IntroduceParameterObjectProcessor processor = + new IntroduceParameterObjectProcessor(getProject(), + classDescriptor, + method, + mergedParams, + parameters, + false); processor.run(); LocalFileSystem.getInstance().refresh(false); FileDocumentManager.getInstance().saveAllDocuments(); diff --git a/platform/lang-api/src/com/intellij/refactoring/changeSignature/OverriderMethodUsageInfo.java b/platform/lang-api/src/com/intellij/refactoring/changeSignature/OverriderMethodUsageInfo.java new file mode 100644 index 000000000000..eee237620ebc --- /dev/null +++ b/platform/lang-api/src/com/intellij/refactoring/changeSignature/OverriderMethodUsageInfo.java @@ -0,0 +1,27 @@ +/* + * 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.changeSignature; + +import com.intellij.psi.PsiNamedElement; + +/** + * Marker interface to distinguish overrider usages during change signature refactoring + */ +public interface OverriderMethodUsageInfo { + T getBaseMethod(); + + T getOverridingMethod(); +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectClassDescriptor.java b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectClassDescriptor.java new file mode 100644 index 000000000000..61729453e934 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectClassDescriptor.java @@ -0,0 +1,99 @@ +/* + * 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.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.changeSignature.ParameterInfo; + +public abstract class IntroduceParameterObjectClassDescriptor { + private final String myClassName; + private final String myPackageName; + private final boolean myUseExistingClass; + private final boolean myCreateInnerClass; + private final String myNewVisibility; + private final boolean myGenerateAccessors; + private final P[] myParamsToMerge; + private PsiElement myExistingClass; + + public IntroduceParameterObjectClassDescriptor(String className, + String packageName, + boolean useExistingClass, + boolean createInnerClass, + String newVisibility, + boolean generateAccessors, + P[] parameters) { + myClassName = className; + myPackageName = packageName; + myUseExistingClass = useExistingClass; + myCreateInnerClass = createInnerClass; + myNewVisibility = newVisibility; + myGenerateAccessors = generateAccessors; + myParamsToMerge = parameters; + } + + public String getClassName() { + return myClassName; + } + + public String getPackageName() { + return myPackageName; + } + + public boolean isUseExistingClass() { + return myUseExistingClass; + } + + public boolean isCreateInnerClass() { + return myCreateInnerClass; + } + + public String getNewVisibility() { + return myNewVisibility; + } + + public P[] getParamsToMerge() { + return myParamsToMerge; + } + + public PsiElement getExistingClass() { + return myExistingClass; + } + + public void setExistingClass(PsiElement existingClass) { + myExistingClass = existingClass; + } + + public boolean isGenerateAccessors() { + return myGenerateAccessors; + } + + public P getParameterInfo(int parameterIdx) { + for (P info : myParamsToMerge) { + if (info.getOldIndex() == parameterIdx) { + return info; + } + } + return null; + } + + public abstract String getSetterName(P paramInfo, PsiElement context); + public abstract String getGetterName(P paramInfo, PsiElement context); + + public abstract void initExistingClass(M method); + + public abstract PsiElement createClass(M method, IntroduceParameterObjectDelegate.Accessor[] accessors); +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java new file mode 100644 index 000000000000..036296f71392 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectDelegate.java @@ -0,0 +1,72 @@ +/* + * 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.lang.Language; +import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.changeSignature.ChangeInfo; +import com.intellij.refactoring.changeSignature.ParameterInfo; +import com.intellij.refactoring.util.FixableUsageInfo; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +public abstract class IntroduceParameterObjectDelegate> { + public enum Accessor { + Getter, Setter; + + } + private static final LanguageExtension EP_NAME = + new LanguageExtension("com.intellij.refactoring.introduceParameterObject"); + + public static > IntroduceParameterObjectDelegate findDelegate( + @NotNull PsiElement element) { + return findDelegate(element.getLanguage()); + } + + public static > IntroduceParameterObjectDelegate findDelegate( + Language language) { + return EP_NAME.forLanguage(language); + } + + public abstract P createMergedParameterInfo(Project project, + C descriptor, + int[] paramsToMerge, + M method); + + public abstract ChangeInfo createChangeSignatureInfo(M method, + List

infos, boolean delegate); + + public abstract + Accessor collectInternalUsages(Collection usages, + M overridingMethod, + M1 element, + IntroduceParameterObjectClassDescriptor classDescriptor, int parameterIdx, + String mergedParamName); + + public abstract void collectAccessibilityUsages(Collection usages, + M method, C descriptor, + Accessor[] accessors); + + public abstract void collectConflicts(MultiMap conflicts, + UsageInfo[] infos, M method, C classDescriptor); +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectProcessor.java new file mode 100644 index 000000000000..4acc7d1f36bd --- /dev/null +++ b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectProcessor.java @@ -0,0 +1,199 @@ +/* + * 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.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.changeSignature.ChangeInfo; +import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; +import com.intellij.refactoring.changeSignature.OverriderMethodUsageInfo; +import com.intellij.refactoring.changeSignature.ParameterInfo; +import com.intellij.refactoring.util.FixableUsageInfo; +import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class IntroduceParameterObjectProcessor> + extends FixableUsagesRefactoringProcessor { + private static final Logger LOG = Logger.getInstance("#" + IntroduceParameterObjectProcessor.class.getName()); + private final C myClassDescriptor; + private final M myMethod; + private final P[] myParameterInfos; + private final ChangeInfo myChangeInfo; + private final P myMergedParameterInfo; + private final IntroduceParameterObjectDelegate myDelegate; + private final IntroduceParameterObjectDelegate.Accessor[] myAccessors; + + public IntroduceParameterObjectProcessor(Project project, + C classDescriptor, + M method, + P[] parameterInfos, + List

parameters, + boolean keepMethodAsDelegate) { + super(project); + myClassDescriptor = classDescriptor; + myMethod = method; + myParameterInfos = parameterInfos; + + int[] paramsToMerge = new int[parameterInfos.length]; + for (int i = 0; i < paramsToMerge.length; i++) { + paramsToMerge[i] = parameterInfos[i].getOldIndex(); + } + + List

newParams = new ArrayList<>(); + int anchor = -1; + for (P param : parameters) { + final int i = ArrayUtil.find(paramsToMerge, param.getOldIndex()); + if (i > -1) { + if (anchor == -1) { + anchor = i; + } + } + else { + newParams.add(param); + } + } + + myDelegate = IntroduceParameterObjectDelegate.findDelegate(method); + LOG.assertTrue(myDelegate != null); + myMergedParameterInfo = myDelegate.createMergedParameterInfo(project, classDescriptor, paramsToMerge, method); + newParams.add(anchor, myMergedParameterInfo); + + myChangeInfo = myDelegate.createChangeSignatureInfo(myMethod, newParams, keepMethodAsDelegate); + myAccessors = new IntroduceParameterObjectDelegate.Accessor[parameterInfos.length]; + } + + @Override + protected void findUsages(@NotNull List usages) { + if (myClassDescriptor.isUseExistingClass()) { + myClassDescriptor.initExistingClass(myMethod); + } + List methodHierarchy = new ArrayList<>(); + methodHierarchy.add(myMethod); + for (UsageInfo info : ChangeSignatureProcessorBase.findUsages(myChangeInfo)) { + if (info instanceof OverriderMethodUsageInfo) { + methodHierarchy.add(((OverriderMethodUsageInfo)info).getOverridingMethod()); + } + usages.add(new ChangeSignatureUsageWrapper(info)); + } + + for (PsiElement element : methodHierarchy) { + final IntroduceParameterObjectDelegate delegate = IntroduceParameterObjectDelegate.findDelegate(element); + if (delegate != null) { + for (int i = 0; i < myParameterInfos.length; i++) { + P parameterInfo = myParameterInfos[i]; + final IntroduceParameterObjectDelegate.Accessor accessor = + delegate.collectInternalUsages(usages, (PsiNamedElement)element, myMethod, myClassDescriptor, parameterInfo.getOldIndex(), + myMergedParameterInfo.getName()); + if (myAccessors[i] == null || accessor == IntroduceParameterObjectDelegate.Accessor.Setter) { + myAccessors[i] = accessor; + } + } + } + } + + myDelegate.collectAccessibilityUsages(usages, myMethod, myClassDescriptor, myAccessors); + } + + @Override + protected boolean preprocessUsages(@NotNull Ref refUsages) { + final UsageInfo[] usageInfos = refUsages.get(); + MultiMap conflicts = new MultiMap(); + myDelegate.collectConflicts(conflicts, usageInfos, myMethod, myClassDescriptor); + + List changeSignatureUsages = new ArrayList<>(); + for (UsageInfo usageInfo : usageInfos) { + if (usageInfo instanceof ChangeSignatureUsageWrapper) { + final UsageInfo info = ((ChangeSignatureUsageWrapper)usageInfo).getInfo(); + if (info instanceof OverriderMethodUsageInfo) { + final PsiElement overridingMethod = ((OverriderMethodUsageInfo)info).getOverridingMethod(); + if (IntroduceParameterObjectDelegate.findDelegate(overridingMethod) == null) { + conflicts.putValue(overridingMethod, "Not supported overrider detected"); + } + } + changeSignatureUsages.add(info); + } + else if (usageInfo instanceof FixableUsageInfo) { + final String conflictMessage = ((FixableUsageInfo)usageInfo).getConflictMessage(); + if (conflictMessage != null) { + conflicts.putValue(usageInfo.getElement(), conflictMessage); + } + } + } + + ChangeSignatureProcessorBase + .collectConflictsFromExtensions(new Ref<>(changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])), conflicts, + myChangeInfo); + + return showConflicts(conflicts, usageInfos); + } + + @NotNull + @Override + protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usages) { + return new IntroduceParameterObjectUsageViewDescriptor(myMethod); + } + + @Override + protected void performRefactoring(@NotNull UsageInfo[] usageInfos) { + final PsiElement aClass = myClassDescriptor.createClass(myMethod, myAccessors); + if (aClass != null) { + myClassDescriptor.setExistingClass(aClass); + super.performRefactoring(usageInfos); + List changeSignatureUsages = new ArrayList<>(); + for (UsageInfo info : usageInfos) { + if (info instanceof ChangeSignatureUsageWrapper) { + changeSignatureUsages.add(((ChangeSignatureUsageWrapper)info).getInfo()); + } + } + ChangeSignatureProcessorBase + .doChangeSignature(myChangeInfo, changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])); + } + } + + protected String getCommandName() { + return RefactoringBundle + .message("refactoring.introduce.parameter.object.command.name", myClassDescriptor.getClassName(), myMethod.getName()); + } + + public static class ChangeSignatureUsageWrapper extends FixableUsageInfo { + private final UsageInfo myInfo; + + public ChangeSignatureUsageWrapper(UsageInfo info) { + super(info.getElement()); + myInfo = info; + } + + public UsageInfo getInfo() { + return myInfo; + } + + @Override + public void fixUsage() throws IncorrectOperationException { + } + } +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectUsageViewDescriptor.java b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectUsageViewDescriptor.java new file mode 100644 index 000000000000..03c72e346b78 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/refactoring/introduceParameterObject/IntroduceParameterObjectUsageViewDescriptor.java @@ -0,0 +1,45 @@ +/* + * 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.PsiElement; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; +import com.intellij.usageView.UsageViewBundle; +import org.jetbrains.annotations.NotNull; + +public class IntroduceParameterObjectUsageViewDescriptor extends UsageViewDescriptorAdapter { + + private final PsiElement method; + + public IntroduceParameterObjectUsageViewDescriptor(PsiElement method) { + this.method = method; + } + + @NotNull + public PsiElement[] getElements() { + return new PsiElement[]{method}; + } + + public String getProcessedElementsHeader() { + return RefactoringBundle.message("refactoring.introduce.parameter.object.method.whose.parameters.are.to.wrapped"); + } + + public String getCodeReferencesText(int usagesCount, int filesCount) { + return RefactoringBundle.message("refactoring.introduce.parameter.object.references.to.be.modified") + + UsageViewBundle.getReferencesString(usagesCount, filesCount); + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/util/FixableUsageInfo.java b/platform/lang-impl/src/com/intellij/refactoring/util/FixableUsageInfo.java similarity index 100% rename from java/java-impl/src/com/intellij/refactoring/util/FixableUsageInfo.java rename to platform/lang-impl/src/com/intellij/refactoring/util/FixableUsageInfo.java diff --git a/java/java-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java similarity index 93% rename from java/java-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java rename to platform/lang-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java index d3175c3b7051..e7bb56ae7699 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/util/FixableUsagesRefactoringProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -18,12 +18,12 @@ package com.intellij.refactoring.util; 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.PsiElement; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.MultiMap; -import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -67,7 +67,7 @@ public abstract class FixableUsagesRefactoringProcessor extends BaseRefactoringP for (UsageInfo info : refUsages.get()) { final String conflict = ((FixableUsageInfo)info).getConflictMessage(); if (conflict != null) { - conflicts.putValue(info.getElement(), XmlUtil.escape(conflict)); + conflicts.putValue(info.getElement(), StringUtil.escapeXml(conflict)); } } } diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 5c3113df826b..28064e8969c9 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -796,4 +796,7 @@ renames.project=Renames project introduce.functional.parameter.title=Extract Functional Parameter introduce.parameter.convert.lambda=&Convert to functional expression -expand.method.reference.warning=Method is used in method reference. Proceeding would result in conversion to lambda expression \ No newline at end of file +expand.method.reference.warning=Method is used in method reference. Proceeding would result in conversion to lambda expression +refactoring.introduce.parameter.object.references.to.be.modified=References to be modified +refactoring.introduce.parameter.object.method.whose.parameters.are.to.wrapped=Method whose parameters are to wrapped +refactoring.introduce.parameter.object.command.name=Introduced parameter class {0} for {1}() diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index c6415dd6817f..eb56f84c9c5a 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -375,6 +375,11 @@ + + + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 423f65555023..187971899394 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1731,6 +1731,8 @@ +