blob: f1c71336e9f2fe65dbb9e6a33c142e66df2e4296 [file] [log] [blame]
/*
* 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.changeSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
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.impl.source.tree.Factory;
import com.intellij.psi.impl.source.tree.SharedImplUtil;
import com.intellij.psi.scope.processor.VariablesProcessor;
import com.intellij.psi.util.*;
import com.intellij.refactoring.changeSignature.*;
import com.intellij.refactoring.rename.ResolveSnapshotProvider;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocParameterReference;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocTag;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
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.GrCodeBlock;
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.GrSafeCastExpression;
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.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.refactoring.DefaultGroovyVariableNameValidator;
import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.List;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
public class GrChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
private static final Logger LOG =
Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.changeSignature.GrChangeSignatureUsageProcessor");
@Override
public UsageInfo[] findUsages(ChangeInfo info) {
if (info instanceof JavaChangeInfo) {
return new GrChageSignatureUsageSearcher((JavaChangeInfo)info).findUsages();
}
return UsageInfo.EMPTY_ARRAY;
}
@Override
public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
if (info instanceof JavaChangeInfo) {
return new GrChangeSignatureConflictSearcher((JavaChangeInfo)info).findConflicts(refUsages);
}
else {
return new MultiMap<PsiElement, String>();
}
}
@Override
public boolean processPrimaryMethod(ChangeInfo changeInfo) {
if (!(changeInfo instanceof GrChangeInfoImpl)) return false;
GrChangeInfoImpl grInfo = (GrChangeInfoImpl)changeInfo;
GrMethod method = grInfo.getMethod();
if (grInfo.isGenerateDelegate()) {
return generateDelegate(grInfo);
}
return processPrimaryMethodInner(grInfo, method, null);
}
@Override
public boolean shouldPreviewUsages(ChangeInfo changeInfo, UsageInfo[] usages) {
if (!StringUtil.isJavaIdentifier(changeInfo.getNewName())) return true;
for (UsageInfo usage : usages) {
if (usage instanceof GrMethodCallUsageInfo) {
if (((GrMethodCallUsageInfo)usage).isPossibleUsage()) return true;
}
}
return false;
}
@Override
public boolean setupDefaultValues(ChangeInfo changeInfo, Ref<UsageInfo[]> refUsages, Project project) {
if (!(changeInfo instanceof JavaChangeInfo)) return true;
for (UsageInfo usageInfo : refUsages.get()) {
if (usageInfo instanceof GrMethodCallUsageInfo) {
GrMethodCallUsageInfo methodCallUsageInfo = (GrMethodCallUsageInfo)usageInfo;
if (methodCallUsageInfo.isToChangeArguments()){
final PsiElement element = methodCallUsageInfo.getElement();
if (element == null) continue;
final PsiMethod caller = RefactoringUtil.getEnclosingMethod(element);
final boolean needDefaultValue = !((JavaChangeInfo)changeInfo).getMethodsToPropagateParameters().contains(caller);
final PsiMethod referencedMethod = methodCallUsageInfo.getReferencedMethod();
if (needDefaultValue &&
(caller == null || referencedMethod == null || !MethodSignatureUtil.isSuperMethod(referencedMethod, caller))) {
final ParameterInfo[] parameters = changeInfo.getNewParameters();
for (ParameterInfo parameter : parameters) {
final String defaultValue = parameter.getDefaultValue();
if (defaultValue == null && parameter.getOldIndex() == -1) {
((ParameterInfoImpl)parameter).setDefaultValue("");
if (!ApplicationManager.getApplication().isUnitTestMode()) {
final PsiType type = ((ParameterInfoImpl)parameter).getTypeWrapper().getType(element, element.getManager());
final DefaultValueChooser chooser = new DefaultValueChooser(project, parameter.getName(), PsiTypesUtil.getDefaultValueOfType(type));
chooser.show();
if (chooser.isOK()) {
if (chooser.feelLucky()) {
parameter.setUseAnySingleVariable(true);
} else {
((ParameterInfoImpl)parameter).setDefaultValue(chooser.getDefaultValue());
}
} else {
return false;
}
}
}
}
}
}
}
}
return true;
}
@Override
public void registerConflictResolvers(List<ResolveSnapshotProvider.ResolveSnapshot> snapshots,
@NotNull ResolveSnapshotProvider resolveSnapshotProvider,
UsageInfo[] usages, ChangeInfo changeInfo) {
}
private static boolean generateDelegate(GrChangeInfoImpl grInfo) {
final GrMethod method = grInfo.getMethod();
final PsiClass psiClass = method.getContainingClass();
GrMethod newMethod = (GrMethod)method.copy();
newMethod = (GrMethod)psiClass.addAfter(newMethod, method);
StringBuilder buffer = new StringBuilder();
buffer.append("\n");
if (method.isConstructor()) {
buffer.append("this");
}
else {
if (!PsiType.VOID.equals(method.getReturnType())) {
buffer.append("return ");
}
buffer.append(GrChangeSignatureUtil.getNameWithQuotesIfNeeded(grInfo.getNewName(), method.getProject()));
}
generateParametersForDelegateCall(grInfo, method, buffer);
final GrCodeBlock codeBlock = GroovyPsiElementFactory.getInstance(method.getProject()).createMethodBodyFromText(buffer.toString());
newMethod.setBlock(codeBlock);
newMethod.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false);
CodeStyleManager.getInstance(method.getProject()).reformat(newMethod);
return processPrimaryMethodInner(grInfo, method, null);
}
private static void generateParametersForDelegateCall(GrChangeInfoImpl grInfo, GrMethod method, StringBuilder buffer) {
buffer.append("(");
final GrParameter[] oldParameters = method.getParameterList().getParameters();
final JavaParameterInfo[] parameters = grInfo.getNewParameters();
String[] params = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
JavaParameterInfo parameter = parameters[i];
final int oldIndex = parameter.getOldIndex();
if (oldIndex >= 0) {
params[i] = oldParameters[oldIndex].getName();
}
else {
params[i] = parameter.getDefaultValue();
}
}
buffer.append(StringUtil.join(params, ","));
buffer.append(");");
}
private static boolean processPrimaryMethodInner(JavaChangeInfo changeInfo, GrMethod method, @Nullable PsiMethod baseMethod) {
if (changeInfo.isNameChanged()) {
String newName = baseMethod == null ? changeInfo.getNewName() :
RefactoringUtil.suggestNewOverriderName(method.getName(), baseMethod.getName(), changeInfo.getNewName());
if (newName != null && !newName.equals(method.getName())) {
method.setName(changeInfo.getNewName());
}
}
final GrModifierList modifierList = method.getModifierList();
if (changeInfo.isVisibilityChanged()) {
modifierList.setModifierProperty(changeInfo.getNewVisibility(), true);
}
PsiSubstitutor substitutor = baseMethod != null ? calculateSubstitutor(method, baseMethod) : PsiSubstitutor.EMPTY;
final PsiMethod context = changeInfo.getMethod();
GrTypeElement oldReturnTypeElement = method.getReturnTypeElementGroovy();
if (changeInfo.isReturnTypeChanged()) {
CanonicalTypes.Type newReturnType = changeInfo.getNewReturnType();
if (newReturnType == null) {
if (oldReturnTypeElement != null) {
oldReturnTypeElement.delete();
if (modifierList.getModifiers().length == 0) {
modifierList.setModifierProperty(GrModifier.DEF, true);
}
}
}
else {
PsiType type = newReturnType.getType(context, method.getManager());
GrReferenceAdjuster.shortenAllReferencesIn(method.setReturnType(substitutor.substitute(type)));
if (oldReturnTypeElement == null) {
modifierList.setModifierProperty(GrModifier.DEF, false);
}
}
}
JavaParameterInfo[] newParameters = changeInfo.getNewParameters();
final GrParameterList parameterList = method.getParameterList();
GrParameter[] oldParameters = parameterList.getParameters();
final PsiParameter[] oldBaseParams = baseMethod != null ? baseMethod.getParameterList().getParameters() : null;
Set<GrParameter> toRemove = new HashSet<GrParameter>(oldParameters.length);
ContainerUtil.addAll(toRemove, oldParameters);
GrParameter anchor = null;
final GrDocComment docComment = method.getDocComment();
final GrDocTag[] tags = docComment == null ? null : docComment.getTags();
for (JavaParameterInfo newParameter : newParameters) {
//if old parameter name differs from base method parameter name we don't change it
final String newName;
final int oldIndex = newParameter.getOldIndex();
if (oldIndex >= 0 && oldBaseParams != null) {
final String oldName = oldParameters[oldIndex].getName();
if (oldName.equals(oldBaseParams[oldIndex].getName())) {
newName = newParameter.getName();
}
else {
newName = oldName;
}
}
else {
newName = newParameter.getName();
}
final GrParameter oldParameter = oldIndex >= 0 ? oldParameters[oldIndex] : null;
if (docComment != null && oldParameter != null) {
final String oldName = oldParameter.getName();
for (GrDocTag tag : tags) {
if ("@param".equals(tag.getName())) {
final GrDocParameterReference parameterReference = tag.getDocParameterReference();
if (parameterReference != null && oldName.equals(parameterReference.getText())) {
parameterReference.handleElementRename(newName);
}
}
}
}
GrParameter grParameter = createNewParameter(substitutor, context, parameterList, newParameter, newName);
if (oldParameter != null) {
grParameter.getModifierList().replace(oldParameter.getModifierList());
}
if ("def".equals(newParameter.getTypeText())) {
grParameter.getModifierList().setModifierProperty(GrModifier.DEF, true);
}
else if (StringUtil.isEmpty(newParameter.getTypeText())) {
grParameter.getModifierList().setModifierProperty(GrModifier.DEF, false);
}
anchor = (GrParameter)parameterList.addAfter(grParameter, anchor);
}
for (GrParameter oldParameter : toRemove) {
oldParameter.delete();
}
JavaCodeStyleManager.getInstance(parameterList.getProject()).shortenClassReferences(parameterList);
CodeStyleManager.getInstance(parameterList.getProject()).reformat(parameterList);
if (changeInfo.isExceptionSetOrOrderChanged()) {
final ThrownExceptionInfo[] infos = changeInfo.getNewExceptions();
PsiClassType[] exceptionTypes = new PsiClassType[infos.length];
for (int i = 0; i < infos.length; i++) {
ThrownExceptionInfo info = infos[i];
exceptionTypes[i] = (PsiClassType)info.createType(method, method.getManager());
}
PsiReferenceList thrownList = GroovyPsiElementFactory.getInstance(method.getProject()).createThrownList(exceptionTypes);
thrownList = (PsiReferenceList)method.getThrowsList().replace(thrownList);
JavaCodeStyleManager.getInstance(thrownList.getProject()).shortenClassReferences(thrownList);
CodeStyleManager.getInstance(method.getProject()).reformat(method.getThrowsList());
}
return true;
}
private static GrParameter createNewParameter(@NotNull PsiSubstitutor substitutor,
@NotNull PsiMethod context,
@NotNull GrParameterList parameterList,
@NotNull JavaParameterInfo newParameter,
@NotNull String newName) {
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(parameterList.getProject());
String typeText = newParameter.getTypeText();
if (newParameter instanceof GrParameterInfo && (typeText.isEmpty() || "def".equals(typeText))) {
return factory.createParameter(newName, null, getInitializer(newParameter), parameterList);
}
PsiType type = substitutor.substitute(newParameter.createType(context, parameterList.getManager()));
return factory.createParameter(newName, type == null ? null : type.getCanonicalText(), getInitializer(newParameter), parameterList);
}
private static PsiSubstitutor calculateSubstitutor(PsiMethod derivedMethod, PsiMethod baseMethod) {
PsiSubstitutor substitutor;
if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
substitutor = PsiSubstitutor.EMPTY;
}
else {
final PsiClass baseClass = baseMethod.getContainingClass();
final PsiClass derivedClass = derivedMethod.getContainingClass();
if (baseClass != null && derivedClass != null && InheritanceUtil.isInheritorOrSelf(derivedClass, baseClass, true)) {
final PsiSubstitutor superClassSubstitutor =
TypeConversionUtil.getSuperClassSubstitutor(baseClass, derivedClass, PsiSubstitutor.EMPTY);
final MethodSignature superMethodSignature = baseMethod.getSignature(superClassSubstitutor);
final MethodSignature methodSignature = derivedMethod.getSignature(PsiSubstitutor.EMPTY);
final PsiSubstitutor superMethodSubstitutor =
MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superMethodSignature);
substitutor = superMethodSubstitutor != null ? superMethodSubstitutor : superClassSubstitutor;
}
else {
substitutor = PsiSubstitutor.EMPTY;
}
}
return substitutor;
}
@Nullable
private static <Type extends PsiElement, List extends PsiElement> Type getNextOfType(List parameterList,
PsiElement current,
Class<Type> type) {
return current != null ? PsiTreeUtil.getNextSiblingOfType(current, type) : PsiTreeUtil.getChildOfType(parameterList, type);
}
@Nullable
private static String getInitializer(JavaParameterInfo newParameter) {
if (newParameter instanceof GrParameterInfo) return ((GrParameterInfo)newParameter).getDefaultInitializer();
return null;
}
@Override
public boolean processUsage(ChangeInfo changeInfo, UsageInfo usageInfo, boolean beforeMethodChange, UsageInfo[] usages) {
if (!(changeInfo instanceof JavaChangeInfo)) return false;
PsiElement element = usageInfo.getElement();
if (element == null) return false;
if (!GroovyLanguage.INSTANCE.equals(element.getLanguage())) return false;
if (beforeMethodChange) {
if (usageInfo instanceof OverriderUsageInfo) {
processPrimaryMethodInner(((JavaChangeInfo)changeInfo), (GrMethod)((OverriderUsageInfo)usageInfo).getElement(),
((OverriderUsageInfo)usageInfo).getBaseMethod());
}
}
else {
if (usageInfo instanceof GrMethodCallUsageInfo) {
processMethodUsage(element, ((JavaChangeInfo)changeInfo), ((GrMethodCallUsageInfo)usageInfo).isToChangeArguments(),
((GrMethodCallUsageInfo)usageInfo).isToCatchExceptions(),
((GrMethodCallUsageInfo)usageInfo).getMapToArguments(), ((GrMethodCallUsageInfo)usageInfo).getSubstitutor());
return true;
}
else if (usageInfo instanceof DefaultConstructorImplicitUsageInfo) {
processConstructor(
(GrMethod)((DefaultConstructorImplicitUsageInfo)usageInfo).getConstructor(),
(JavaChangeInfo)changeInfo);
return true;
}
else if (usageInfo instanceof NoConstructorClassUsageInfo) {
processClassUsage((GrTypeDefinition)((NoConstructorClassUsageInfo)usageInfo).getPsiClass(), ((JavaChangeInfo)changeInfo));
return true;
}
else if (usageInfo instanceof ChangeSignatureParameterUsageInfo) {
String newName = ((ChangeSignatureParameterUsageInfo)usageInfo).newParameterName;
((PsiReference)element).handleElementRename(newName);
return true;
}
else {
PsiReference ref = element.getReference();
if (ref != null && changeInfo.getMethod() != null) {
ref.bindToElement(changeInfo.getMethod());
return true;
}
}
}
return false;
}
private static void processClassUsage(GrTypeDefinition psiClass, JavaChangeInfo changeInfo) {
String name = psiClass.getName();
GrMethod constructor = GroovyPsiElementFactory.getInstance(psiClass.getProject())
.createConstructorFromText(name, ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_STRING_ARRAY, "{}", null);
GrModifierList list = constructor.getModifierList();
if (psiClass.hasModifierProperty(PsiModifier.PRIVATE)) list.setModifierProperty(PsiModifier.PRIVATE, true);
if (psiClass.hasModifierProperty(PsiModifier.PROTECTED)) list.setModifierProperty(PsiModifier.PROTECTED, true);
if (!list.hasExplicitVisibilityModifiers()) {
list.setModifierProperty(GrModifier.DEF, true);
}
constructor = (GrMethod)psiClass.add(constructor);
processConstructor(constructor, changeInfo);
}
private static void processConstructor(GrMethod constructor, JavaChangeInfo changeInfo) {
final PsiClass containingClass = constructor.getContainingClass();
final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, containingClass, PsiSubstitutor.EMPTY);
GrOpenBlock block = constructor.getBlock();
GrConstructorInvocation invocation =
GroovyPsiElementFactory.getInstance(constructor.getProject()).createConstructorInvocation("super()");
invocation = (GrConstructorInvocation)block.addStatementBefore(invocation, getFirstStatement(block));
processMethodUsage(invocation.getInvokedExpression(), changeInfo,
changeInfo.isParameterSetOrOrderChanged() || changeInfo.isParameterNamesChanged(),
changeInfo.isExceptionSetChanged(), GrClosureSignatureUtil.ArgInfo.<PsiElement>empty_array(), substitutor);
}
@Nullable
private static GrStatement getFirstStatement(GrCodeBlock block) {
GrStatement[] statements = block.getStatements();
if (statements.length == 0) return null;
return statements[0];
}
private static void processMethodUsage(PsiElement element,
JavaChangeInfo changeInfo,
boolean toChangeArguments,
boolean toCatchExceptions,
GrClosureSignatureUtil.ArgInfo<PsiElement>[] map,
PsiSubstitutor substitutor) {
if (map == null) return;
if (changeInfo.isNameChanged()) {
if (element instanceof GrReferenceElement) {
element = ((GrReferenceElement)element).handleElementRename(changeInfo.getNewName());
}
}
if (toChangeArguments) {
JavaParameterInfo[] parameters = changeInfo.getNewParameters();
GrArgumentList argumentList = PsiUtil.getArgumentsList(element);
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(element.getProject());
if (argumentList == null) {
if (element instanceof GrEnumConstant) {
argumentList = factory.createArgumentList();
argumentList = (GrArgumentList)element.add(argumentList);
}
else {
return;
}
}
Set<PsiElement> argsToDelete = new HashSet<PsiElement>(map.length * 2);
for (GrClosureSignatureUtil.ArgInfo<PsiElement> argInfo : map) {
argsToDelete.addAll(argInfo.args);
}
for (JavaParameterInfo parameter : parameters) {
int index = parameter.getOldIndex();
if (index >= 0) {
argsToDelete.removeAll(map[index].args);
}
}
for (PsiElement arg : argsToDelete) {
arg.delete();
}
boolean skipOptionals = false;
PsiElement anchor = null; //PsiTreeUtil.getChildOfAnyType(argumentList, GrExpression.class, GrNamedArgument.class);
for (int i = 0; i < parameters.length; i++) {
JavaParameterInfo parameter = parameters[i];
int index = parameter.getOldIndex();
if (index >= 0) {
GrClosureSignatureUtil.ArgInfo<PsiElement> argInfo = map[index];
List<PsiElement> arguments = argInfo.args;
if (argInfo.isMultiArg) { //arguments for Map and varArg
if ((i != 0 || !(!arguments.isEmpty() && arguments.iterator().next() instanceof GrNamedArgument)) &&
(i != parameters.length - 1 || !parameter.isVarargType())) {
final PsiType type = parameter.createType(changeInfo.getMethod().getParameterList(), argumentList.getManager());
final GrExpression arg = GroovyRefactoringUtil.generateArgFromMultiArg(substitutor, arguments, type, element.getProject());
for (PsiElement argument : arguments) {
argument.delete();
}
anchor = argumentList.addAfter(arg, anchor);
JavaCodeStyleManager.getInstance(anchor.getProject()).shortenClassReferences(anchor);
}
}
else { //arguments for simple parameters
if (arguments.size() == 1) { //arg exists
PsiElement arg = arguments.iterator().next();
if (i == parameters.length - 1 && parameter.isVarargType()) {
if (arg instanceof GrSafeCastExpression) {
PsiElement expr = ((GrSafeCastExpression)arg).getOperand();
if (expr instanceof GrListOrMap && !((GrListOrMap)expr).isMap()) {
final PsiElement copy = expr.copy();
PsiElement[] newVarargs = ((GrListOrMap)copy).getInitializers();
for (PsiElement vararg : newVarargs) {
anchor = argumentList.addAfter(vararg, anchor);
}
arg.delete();
continue;
}
}
}
PsiElement curArg = getNextOfType(argumentList, anchor, GrExpression.class);
if (curArg == arg) {
anchor = arg;
}
else {
final PsiElement copy = arg.copy();
anchor = argumentList.addAfter(copy, anchor);
arg.delete();
}
}
else { //arg is skipped. Parameter is optional
skipOptionals = true;
}
}
}
else {
if (skipOptionals && isParameterOptional(parameter)) continue;
if (forceOptional(parameter)) {
skipOptionals = true;
continue;
}
try {
GrExpression value = createDefaultValue(factory, changeInfo, parameter, argumentList);
if (i > 0 && (value == null || anchor == null)) {
PsiElement comma = Factory.createSingleLeafElement(GroovyTokenTypes.mCOMMA, ",", 0, 1,
SharedImplUtil.findCharTableByTree(argumentList.getNode()),
argumentList.getManager()).getPsi();
if (anchor == null) anchor = argumentList.getLeftParen();
anchor = argumentList.addAfter(comma, anchor);
}
if (value != null) {
anchor = argumentList.addAfter(value, anchor);
}
}
catch (IncorrectOperationException e) {
LOG.error(e.getMessage());
}
}
}
GrCall call = GroovyRefactoringUtil.getCallExpressionByMethodReference(element);
if (argumentList.getText().trim().isEmpty() && (call == null || !PsiImplUtil.hasClosureArguments(call))) {
argumentList = argumentList.replaceWithArgumentList(factory.createArgumentList());
}
CodeStyleManager.getInstance(argumentList.getProject()).reformat(argumentList);
}
if (toCatchExceptions) {
final ThrownExceptionInfo[] exceptionInfos = changeInfo.getNewExceptions();
PsiClassType[] exceptions = getExceptions(exceptionInfos, element, element.getManager());
fixExceptions(element, exceptions);
}
}
@Nullable
private static GrExpression createDefaultValue(GroovyPsiElementFactory factory,
JavaChangeInfo changeInfo,
JavaParameterInfo info,
final GrArgumentList list) {
if (info.isUseAnySingleVariable()) {
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager());
final VariablesProcessor processor = new VariablesProcessor(false) {
@Override
protected boolean check(PsiVariable var, ResolveState state) {
if (var instanceof PsiField && !resolveHelper.isAccessible((PsiField)var, list, null)) return false;
if (var instanceof GrVariable &&
PsiUtil.isLocalVariable(var) &&
list.getTextRange().getStartOffset() <= var.getTextRange().getStartOffset()) {
return false;
}
if (PsiTreeUtil.isAncestor(var, list, false)) return false;
final PsiType _type = var instanceof GrVariable ? ((GrVariable)var).getTypeGroovy() : var.getType();
final PsiType varType = state.get(PsiSubstitutor.KEY).substitute(_type);
return type.isAssignableFrom(varType);
}
@Override
public boolean execute(@NotNull PsiElement pe, @NotNull ResolveState state) {
super.execute(pe, state);
return size() < 2;
}
};
ResolveUtil.treeWalkUp(list, processor, false);
if (processor.size() == 1) {
final PsiVariable result = processor.getResult(0);
return factory.createExpressionFromText(result.getName(), list);
}
if (processor.size() == 0) {
final PsiClass parentClass = PsiTreeUtil.getParentOfType(list, PsiClass.class);
if (parentClass != null) {
PsiClass containingClass = parentClass;
final Set<PsiClass> containingClasses = new HashSet<PsiClass>();
final PsiElementFactory jfactory = JavaPsiFacade.getElementFactory(list.getProject());
while (containingClass != null) {
if (type.isAssignableFrom(jfactory.createType(containingClass, PsiSubstitutor.EMPTY))) {
containingClasses.add(containingClass);
}
containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class);
}
if (containingClasses.size() == 1) {
return factory.createThisExpression(containingClasses.contains(parentClass) ? null : containingClasses.iterator().next());
}
}
}
}
final String value = info.getDefaultValue();
return !StringUtil.isEmpty(value) ? factory.createExpressionFromText(value, list) : null;
}
protected static boolean forceOptional(JavaParameterInfo parameter) {
return parameter instanceof GrParameterInfo && ((GrParameterInfo)parameter).forceOptional();
}
private static void fixExceptions(PsiElement element, PsiClassType[] exceptions) {
if (exceptions.length == 0) return;
final GroovyPsiElement context =
PsiTreeUtil.getParentOfType(element, GrTryCatchStatement.class, GrClosableBlock.class, GrMethod.class, GroovyFile.class);
if (context instanceof GrClosableBlock) {
element = generateTryCatch(element, exceptions);
}
else if (context instanceof GrMethod) {
final PsiClassType[] handledExceptions = ((GrMethod)context).getThrowsList().getReferencedTypes();
final List<PsiClassType> psiClassTypes = filterOutExceptions(exceptions, context, handledExceptions);
element = generateTryCatch(element, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]));
}
else if (context instanceof GroovyFile) {
element = generateTryCatch(element, exceptions);
}
else if (context instanceof GrTryCatchStatement) {
final GrCatchClause[] catchClauses = ((GrTryCatchStatement)context).getCatchClauses();
List<PsiClassType> referencedTypes = ContainerUtil.map(catchClauses, new Function<GrCatchClause, PsiClassType>() {
@Override
@Nullable
public PsiClassType fun(GrCatchClause grCatchClause) {
final GrParameter grParameter = grCatchClause.getParameter();
final PsiType type = grParameter != null ? grParameter.getType() : null;
if (type instanceof PsiClassType) {
return (PsiClassType)type;
}
else {
return null;
}
}
});
referencedTypes = ContainerUtil.skipNulls(referencedTypes);
final List<PsiClassType> psiClassTypes =
filterOutExceptions(exceptions, context, referencedTypes.toArray(new PsiClassType[referencedTypes.size()]));
element = fixCatchBlock((GrTryCatchStatement)context, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]));
}
// CodeStyleManager.getInstance(element.getProject()).reformat(element);
}
private static PsiElement generateTryCatch(PsiElement element, PsiClassType[] exceptions) {
if (exceptions.length == 0) return element;
GrTryCatchStatement tryCatch =
(GrTryCatchStatement)GroovyPsiElementFactory.getInstance(element.getProject()).createStatementFromText("try{} catch (Exception e){}");
final GrStatement statement = PsiTreeUtil.getParentOfType(element, GrStatement.class);
assert statement != null;
tryCatch.getTryBlock().addStatementBefore(statement, null);
tryCatch = (GrTryCatchStatement)statement.replace(tryCatch);
tryCatch.getCatchClauses()[0].delete();
fixCatchBlock(tryCatch, exceptions);
return tryCatch;
}
private static PsiElement fixCatchBlock(GrTryCatchStatement tryCatch, PsiClassType[] exceptions) {
if (exceptions.length == 0) return tryCatch;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(tryCatch.getProject());
final GrCatchClause[] clauses = tryCatch.getCatchClauses();
List<String> restricted = ContainerUtil.map(clauses, new Function<GrCatchClause, String>() {
@Override
@Nullable
public String fun(GrCatchClause grCatchClause) {
final GrParameter grParameter = grCatchClause.getParameter();
return grParameter != null ? grParameter.getName() : null;
}
});
restricted = ContainerUtil.skipNulls(restricted);
final DefaultGroovyVariableNameValidator nameValidator = new DefaultGroovyVariableNameValidator(tryCatch, restricted);
GrCatchClause anchor = clauses.length == 0 ? null : clauses[clauses.length - 1];
for (PsiClassType type : exceptions) {
final String[] names = GroovyNameSuggestionUtil.suggestVariableNameByType(type, nameValidator);
final GrCatchClause catchClause = factory.createCatchClause(type, names[0]);
final GrStatement printStackTrace = factory.createStatementFromText(names[0] + ".printStackTrace()");
catchClause.getBody().addStatementBefore(printStackTrace, null);
anchor = tryCatch.addCatchClause(catchClause, anchor);
JavaCodeStyleManager.getInstance(anchor.getProject()).shortenClassReferences(anchor);
}
return tryCatch;
}
private static List<PsiClassType> filterOutExceptions(PsiClassType[] exceptions,
final GroovyPsiElement context,
final PsiClassType[] handledExceptions) {
return ContainerUtil.findAll(exceptions, new Condition<PsiClassType>() {
@Override
public boolean value(PsiClassType o) {
if (!InheritanceUtil.isInheritor(o, CommonClassNames.JAVA_LANG_EXCEPTION)) return false;
for (PsiClassType type : handledExceptions) {
if (TypesUtil.isAssignableByMethodCallConversion(type, o, context)) return false;
}
return true;
}
});
}
private static PsiClassType[] getExceptions(ThrownExceptionInfo[] infos, final PsiElement context, final PsiManager manager) {
return ContainerUtil.map(infos, new Function<ThrownExceptionInfo, PsiClassType>() {
@Override
@Nullable
public PsiClassType fun(ThrownExceptionInfo thrownExceptionInfo) {
return (PsiClassType)thrownExceptionInfo.createType(context, manager);
}
}, new PsiClassType[infos.length]);
}
private static boolean isParameterOptional(JavaParameterInfo parameterInfo) {
return parameterInfo instanceof GrParameterInfo && ((GrParameterInfo)parameterInfo).isOptional();
}
}