| /* |
| * Copyright 2000-2014 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy; |
| |
| import com.intellij.codeInsight.ChangeContextUtil; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.javadoc.PsiDocTag; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.impl.ExpressionConverter; |
| import com.intellij.refactoring.introduceParameter.IntroduceParameterData; |
| import com.intellij.refactoring.introduceParameter.IntroduceParameterMethodUsagesProcessor; |
| import com.intellij.refactoring.introduceParameter.IntroduceParameterUtil; |
| import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.VisibilityUtil; |
| import com.intellij.util.containers.MultiMap; |
| import gnu.trove.TIntArrayList; |
| import gnu.trove.TIntProcedure; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.GroovyLanguage; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; |
| import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GroovyIntroduceParameterUtil; |
| |
| /** |
| * @author Maxim.Medvedev |
| * Date: Apr 18, 2009 3:16:24 PM |
| */ |
| public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceParameterMethodUsagesProcessor { |
| private static final Logger LOG = Logger.getInstance(GroovyIntroduceParameterMethodUsagesProcessor.class); |
| |
| private static boolean isGroovyUsage(UsageInfo usage) { |
| final PsiElement el = usage.getElement(); |
| return el != null && GroovyLanguage.INSTANCE.equals(el.getLanguage()); |
| } |
| |
| @Override |
| public boolean isMethodUsage(UsageInfo usage) { |
| return GroovyRefactoringUtil.isMethodUsage(usage.getElement()) && isGroovyUsage(usage); |
| } |
| |
| @Override |
| public void findConflicts(IntroduceParameterData data, UsageInfo[] usages, MultiMap<PsiElement, String> conflicts) { |
| } |
| |
| @Override |
| public boolean processChangeMethodUsage(IntroduceParameterData data, UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException { |
| GrCall callExpression = GroovyRefactoringUtil.getCallExpressionByMethodReference(usage.getElement()); |
| if (callExpression == null) return true; |
| GrArgumentList argList = callExpression.getArgumentList(); |
| GrExpression[] oldArgs = argList.getExpressionArguments(); |
| |
| final GrExpression anchor; |
| if (!data.getMethodToSearchFor().isVarArgs()) { |
| anchor = getLast(oldArgs); |
| } |
| else { |
| final PsiParameter[] parameters = data.getMethodToSearchFor().getParameterList().getParameters(); |
| if (parameters.length > oldArgs.length) { |
| anchor = getLast(oldArgs); |
| } |
| else { |
| final int lastNonVararg = parameters.length - 2; |
| anchor = lastNonVararg >= 0 ? oldArgs[lastNonVararg] : null; |
| } |
| } |
| |
| PsiMethod method = PsiTreeUtil.getParentOfType(argList, PsiMethod.class); |
| |
| GrClosureSignature signature = GrClosureSignatureUtil.createSignature(callExpression); |
| if (signature == null) signature = GrClosureSignatureUtil.createSignature(data.getMethodToSearchFor(), PsiSubstitutor.EMPTY); |
| |
| final GrClosureSignatureUtil.ArgInfo<PsiElement>[] actualArgs = GrClosureSignatureUtil |
| .mapParametersToArguments(signature, callExpression.getNamedArguments(), callExpression.getExpressionArguments(), |
| callExpression.getClosureArguments(), callExpression, false, true); |
| |
| final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(data.getProject()); |
| |
| if (method != null && IntroduceParameterUtil.isMethodInUsages(data, method, usages)) { |
| argList.addAfter(factory.createExpressionFromText(data.getParameterName()), anchor); |
| } |
| else { |
| final PsiElement _expr = data.getParameterInitializer().getExpression(); |
| PsiElement initializer = ExpressionConverter.getExpression(_expr, GroovyLanguage.INSTANCE, data.getProject()); |
| LOG.assertTrue(initializer instanceof GrExpression); |
| |
| GrExpression newArg = GroovyIntroduceParameterUtil.addClosureToCall(initializer, argList); |
| if (newArg == null) { |
| final PsiElement dummy = argList.addAfter(factory.createExpressionFromText("1"), anchor); |
| newArg = ((GrExpression)dummy).replaceWithExpression((GrExpression)initializer, true); |
| } |
| final PsiMethod methodToReplaceIn = data.getMethodToReplaceIn(); |
| new OldReferencesResolver(callExpression, newArg, methodToReplaceIn, data.getReplaceFieldsWithGetters(), initializer, |
| signature, actualArgs, methodToReplaceIn.getParameterList().getParameters()).resolve(); |
| ChangeContextUtil.clearContextInfo(initializer); |
| |
| //newArg can be replaced by OldReferenceResolver |
| if (newArg.isValid()) { |
| JavaCodeStyleManager.getInstance(newArg.getProject()).shortenClassReferences(newArg); |
| CodeStyleManager.getInstance(data.getProject()).reformat(newArg); |
| } |
| } |
| |
| if (actualArgs == null) { |
| removeParamsFromUnresolvedCall(callExpression, data); |
| } |
| else { |
| removeParametersFromCall(actualArgs, data.getParametersToRemove()); |
| } |
| |
| if (argList.getAllArguments().length == 0 && PsiImplUtil.hasClosureArguments(callExpression)) { |
| final GrArgumentList emptyArgList = ((GrMethodCallExpression)factory.createExpressionFromText("foo{}")).getArgumentList(); |
| LOG.assertTrue(emptyArgList != null); |
| argList.replace(emptyArgList); |
| } |
| return false; |
| } |
| |
| @Nullable |
| private static GrExpression getLast(GrExpression[] oldArgs) { |
| GrExpression anchor; |
| if (oldArgs.length > 0) { |
| anchor = oldArgs[oldArgs.length - 1]; |
| } |
| else { |
| anchor = null; |
| } |
| return anchor; |
| } |
| |
| private static void removeParametersFromCall(final GrClosureSignatureUtil.ArgInfo<PsiElement>[] actualArgs,final TIntArrayList parametersToRemove) { |
| parametersToRemove.forEach(new TIntProcedure() { |
| @Override |
| public boolean execute(final int paramNum) { |
| try { |
| final GrClosureSignatureUtil.ArgInfo<PsiElement> actualArg = actualArgs[paramNum]; |
| if (actualArg == null) return true; |
| for (PsiElement arg : actualArg.args) { |
| arg.delete(); |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| return true; |
| } |
| }); |
| } |
| |
| private static void removeParamsFromUnresolvedCall(GrCall callExpression, IntroduceParameterData data) { |
| final GrExpression[] arguments = callExpression.getExpressionArguments(); |
| final GrClosableBlock[] closureArguments = callExpression.getClosureArguments(); |
| final GrNamedArgument[] namedArguments = callExpression.getNamedArguments(); |
| |
| final boolean hasNamedArgs; |
| if (namedArguments.length > 0) { |
| final PsiMethod method = data.getMethodToSearchFor(); |
| final PsiParameter[] parameters = method.getParameterList().getParameters(); |
| if (parameters.length > 0) { |
| final PsiType type = parameters[0].getType(); |
| hasNamedArgs = InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP); |
| } |
| else { |
| hasNamedArgs = false; |
| } |
| } |
| else { |
| hasNamedArgs = false; |
| } |
| |
| data.getParametersToRemove().forEachDescending(new TIntProcedure() { |
| @Override |
| public boolean execute(int paramNum) { |
| try { |
| if (paramNum == 0 && hasNamedArgs) { |
| for (GrNamedArgument namedArgument : namedArguments) { |
| namedArgument.delete(); |
| } |
| } |
| else { |
| if (hasNamedArgs) paramNum--; |
| if (paramNum < arguments.length) { |
| arguments[paramNum].delete(); |
| } |
| else if (paramNum < arguments.length + closureArguments.length) { |
| closureArguments[paramNum - arguments.length].delete(); |
| } |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| return true; |
| } |
| }); |
| } |
| |
| @Override |
| public boolean processChangeMethodSignature(IntroduceParameterData data, UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException { |
| if (!(usage.getElement() instanceof GrMethod) || !isGroovyUsage(usage)) return true; |
| GrMethod method = (GrMethod)usage.getElement(); |
| |
| final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(data.getParameterName(), method.getBlock()); |
| final MethodJavaDocHelper javaDocHelper = new MethodJavaDocHelper(method); |
| |
| final PsiParameter[] parameters = method.getParameterList().getParameters(); |
| data.getParametersToRemove().forEachDescending(new TIntProcedure() { |
| @Override |
| public boolean execute(final int paramNum) { |
| try { |
| PsiParameter param = parameters[paramNum]; |
| PsiDocTag tag = javaDocHelper.getTagForParameter(param); |
| if (tag != null) { |
| tag.delete(); |
| } |
| param.delete(); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| return true; |
| } |
| }); |
| |
| addParameter(method, javaDocHelper, data.getForcedType(), data.getParameterName(), data.isDeclareFinal(), data.getProject()); |
| |
| fieldConflictsResolver.fix(); |
| |
| return false; |
| |
| } |
| |
| @NotNull |
| public static GrParameter addParameter(@NotNull GrParametersOwner parametersOwner, |
| @Nullable MethodJavaDocHelper javaDocHelper, |
| @NotNull PsiType forcedType, |
| @NotNull String parameterName, |
| boolean isFinal, |
| @NotNull Project project) { |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); |
| |
| final String typeText = forcedType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || forcedType == PsiType.NULL ? null : forcedType.getCanonicalText(); |
| |
| GrParameter parameter = factory.createParameter(parameterName, typeText, parametersOwner); |
| parameter.getModifierList().setModifierProperty(PsiModifier.FINAL, isFinal); |
| final PsiParameter anchorParameter = getAnchorParameter(parametersOwner); |
| final GrParameterList parameterList = parametersOwner.getParameterList(); |
| parameter = (GrParameter)parameterList.addAfter(parameter, anchorParameter); |
| |
| JavaCodeStyleManager.getInstance(project).shortenClassReferences(parameter); |
| if (javaDocHelper != null) { |
| final PsiDocTag tagForAnchorParameter = javaDocHelper.getTagForParameter(anchorParameter); |
| javaDocHelper.addParameterAfter(parameterName, tagForAnchorParameter); |
| } |
| |
| return parameter; |
| } |
| |
| @Nullable |
| private static PsiParameter getAnchorParameter(GrParametersOwner parametersOwner) { |
| PsiParameterList parameterList = parametersOwner.getParameterList(); |
| final PsiParameter anchorParameter; |
| final PsiParameter[] parameters = parameterList.getParameters(); |
| final int length = parameters.length; |
| if (!parametersOwner.isVarArgs()) { |
| anchorParameter = length > 0 ? parameters[length - 1] : null; |
| } |
| else { |
| anchorParameter = length > 1 ? parameters[length - 2] : null; |
| } |
| return anchorParameter; |
| } |
| |
| @Override |
| public boolean processAddDefaultConstructor(IntroduceParameterData data, UsageInfo usage, UsageInfo[] usages) { |
| if (!(usage.getElement() instanceof PsiClass) || !isGroovyUsage(usage)) return true; |
| PsiClass aClass = (PsiClass)usage.getElement(); |
| final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(data.getProject()); |
| GrMethod constructor = |
| factory.createConstructorFromText(aClass.getName(), ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_STRING_ARRAY, "{}"); |
| constructor = (GrMethod)aClass.add(constructor); |
| constructor.getModifierList().setModifierProperty(VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true); |
| processAddSuperCall(data, new UsageInfo(constructor), usages); |
| return false; |
| } |
| |
| @Override |
| public boolean processAddSuperCall(IntroduceParameterData data, UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException { |
| if (!(usage.getElement() instanceof GrMethod) || !isGroovyUsage(usage)) return true; |
| GrMethod constructor = (GrMethod)usage.getElement(); |
| |
| if (!constructor.isConstructor()) return true; |
| |
| final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(data.getProject()); |
| |
| GrConstructorInvocation superCall = factory.createConstructorInvocation("super();"); |
| GrOpenBlock body = constructor.getBlock(); |
| final GrStatement[] statements = body.getStatements(); |
| if (statements.length > 0) { |
| superCall = (GrConstructorInvocation)body.addStatementBefore(superCall, statements[0]); |
| } |
| else { |
| superCall = (GrConstructorInvocation)body.addStatementBefore(superCall, null); |
| } |
| processChangeMethodUsage(data, new UsageInfo(superCall), usages); |
| return false; |
| |
| } |
| } |