ExtractClosure from method

This commit is contained in:
Maxim.Medvedev
2012-01-25 11:55:41 +04:00
parent b03eb1b8ef
commit 9f92a3c899
34 changed files with 1860 additions and 472 deletions
+5
View File
@@ -1086,6 +1086,11 @@
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
<action id="Groovy.Extract.Closure" class="org.jetbrains.plugins.groovy.refactoring.extract.closure.ExtractClosureAction" text="Closure...">
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractMethod"/>
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt shift M"/>
</action>
<group id="Mvc.Actions" popup="true" class="org.jetbrains.plugins.groovy.mvc.MvcActionGroup">
<action id="Mvc.Upgrade"
class="org.jetbrains.plugins.groovy.mvc.MvcUpgradeAction"
@@ -166,3 +166,5 @@ reference.to.accessor.0.is.used=Reference to accessor <bold>{0}<bold> cannot be
variable.is.accessed.for.writing=Variable ''{0}'' is accessed for writing
introduce.closure.parameter.elements.header=Adding parameter to a closure
extract.closure.title=Extract closure
cannot.process.usage.in.language.{0}=Cannot process usage in {0}
you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method=You cannot pass ''{0}'' as parameter because you remove ''{1}'' from the method. You should deselect "Remove parameter ''{0}'' no longer used" or deselect parameter ''{1}'' in the parameter table.
@@ -34,9 +34,13 @@ import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
@@ -45,15 +49,17 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefin
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.inline.GroovyInlineMethodUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* @author Max Medvedev
*/
public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> implements RefactoringActionHandler {
public abstract class ExtractHandlerBase implements RefactoringActionHandler {
private static final Logger LOG = Logger.getInstance(ExtractHandlerBase.class);
public void invokeOnEditor(Project project, Editor editor, PsiFile file, int start, int end) throws ExtractException {
@@ -68,12 +74,12 @@ public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> i
SelectionModel selectionModel = editor.getSelectionModel();
PsiDocumentManager.getInstance(project).commitAllDocuments();
PsiElement[] elements = ExtractUtil.getElementsInOffset(file, start, end);
PsiElement[] elements = getElementsInOffset(file, start, end);
if (elements.length == 1 && elements[0] instanceof GrExpression) {
selectionModel.setSelection(start, elements[0].getTextRange().getEndOffset());
}
GrStatement[] statements = ExtractUtil.getStatementsByElements(elements);
GrStatement[] statements = getStatementsByElements(elements);
if (statements.length == 0) {
String message =
@@ -90,8 +96,8 @@ public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> i
}
GrStatement statement0 = statements[0];
GrMemberOwner owner = ExtractUtil.getMemberOwner(statement0);
GrStatementOwner declarationOwner = ExtractUtil.getDeclarationOwner(statement0);
GrMemberOwner owner = getMemberOwner(statement0);
GrStatementOwner declarationOwner = getDeclarationOwner(statement0);
if (owner == null || declarationOwner == null && !ExtractUtil.isSingleExpression(statements)) {
String message =
RefactoringBundle.getCannotRefactorMessage(GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context"));
@@ -145,7 +151,7 @@ public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> i
boolean hasReturns = returnStatements.size() > 0;
List<GrStatement> returnStatementsCopy = new ArrayList<GrStatement>(returnStatements.size());
returnStatementsCopy.addAll(returnStatements);
boolean isReturnStatement = ExtractUtil.isReturnStatement(statements[statements.length - 1], returnStatementsCopy);
boolean isReturnStatement = isReturnStatement(statements[statements.length - 1], returnStatementsCopy);
boolean isLastStatementOfMethod = isLastStatementOfMethodOrClosure(statements);
if (hasReturns && !isLastStatementOfMethod && !isReturnStatement || hasInterruptingStatements) {
String message = GroovyRefactoringBundle.message("refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow");
@@ -154,10 +160,8 @@ public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> i
InitialInfo info = new InitialInfo(inputInfos, outputInfos, elements, statements, owner, returnStatements);
InfoHelper helper = getSettings(info);
if (helper == null) return;
performRefactoring(helper, owner, declarationOwner, editor, statement0);
performRefactoring(info, owner, declarationOwner, editor, statement0);
}
private static boolean isLastStatementOfMethodOrClosure(GrStatement[] statements) {
@@ -175,12 +179,64 @@ public abstract class ExtractHandlerBase<InfoHelper extends ExtractInfoHelper> i
return statement0.getManager().areElementsEquivalent(lastFromBlock, lastStatement);
}
@Nullable
public abstract InfoHelper getSettings(@NotNull final InitialInfo initialInfo);
public abstract void performRefactoring(@NotNull final InfoHelper helper,
public abstract void performRefactoring(@NotNull final InitialInfo initialInfo,
@NotNull final GrMemberOwner owner,
final GrStatementOwner declarationOwner,
final Editor editor,
final PsiElement startElement);
private static GrStatement[] getStatementsByElements(PsiElement[] elements) {
ArrayList<GrStatement> statementList = new ArrayList<GrStatement>();
for (PsiElement element : elements) {
if (element instanceof GrStatement) {
statementList.add(((GrStatement) element));
}
}
return statementList.toArray(new GrStatement[statementList.size()]);
}
private static PsiElement[] getElementsInOffset(PsiFile file, int startOffset, int endOffset) {
PsiElement[] elements;
GrExpression expr = GroovyRefactoringUtil.findElementInRange(file, startOffset, endOffset, GrExpression.class);
if (expr != null) {
PsiElement parent = expr.getParent();
if (expr.getParent() instanceof GrMethodCallExpression || parent instanceof GrIndexProperty) {
expr = ((GrExpression) expr.getParent());
}
elements = new PsiElement[]{expr};
} else {
elements = GroovyRefactoringUtil.findStatementsInRange(file, startOffset, endOffset, true);
}
return elements;
}
@Nullable
private static GrMemberOwner getMemberOwner(GrStatement statement) {
PsiElement parent = statement.getParent();
while (parent != null && !(parent instanceof GrMemberOwner)) {
if (parent instanceof GroovyFileBase) return (GrMemberOwner) ((GroovyFileBase) parent).getScriptClass();
parent = parent.getParent();
}
return parent != null ? ((GrMemberOwner) parent) : null;
}
@Nullable
private static GrStatementOwner getDeclarationOwner(GrStatement statement) {
PsiElement parent = statement.getParent();
return parent instanceof GrStatementOwner ? ((GrStatementOwner) parent) : null;
}
private static boolean isReturnStatement(GrStatement statement, Collection<GrStatement> returnStatements) {
if (statement instanceof GrReturnStatement) return true;
if (statement instanceof GrIfStatement) {
boolean checked = GroovyInlineMethodUtil.checkTailIfStatement(((GrIfStatement)statement), returnStatements);
return checked & returnStatements.size() == 0;
}
if (statement instanceof GrExpression) {
return returnStatements.contains(statement);
}
return false;
}
}
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo;
/**
@@ -34,7 +33,7 @@ public interface ExtractInfoHelper {
ParameterInfo[] getParameterInfos();
@NotNull
VariableInfo[] getOutputNames();
VariableInfo[] getOutputVariableInfos();
@NotNull
String[] getArgumentNames();
@@ -48,6 +47,7 @@ public interface ExtractInfoHelper {
@NotNull
GrStatement[] getStatements();
@NotNull
GrMemberOwner getOwner();
boolean hasReturnValue();
String getName();
}
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo;
import java.util.Collection;
@@ -30,7 +29,7 @@ import java.util.Map;
/**
* @author Max Medvedev
*/
public class ExtractInfoHelperBase implements ExtractInfoHelper {
public abstract class ExtractInfoHelperBase implements ExtractInfoHelper {
protected final InitialInfo myInitialInfo;
protected final Map<String, ParameterInfo> myInputNamesMap;
@@ -66,8 +65,8 @@ public class ExtractInfoHelperBase implements ExtractInfoHelper {
@Override
@NotNull
public VariableInfo[] getOutputNames() {
return myInitialInfo.getOutputNames();
public VariableInfo[] getOutputVariableInfos() {
return myInitialInfo.getOutputVariableInfos();
}
/**
@@ -107,12 +106,6 @@ public class ExtractInfoHelperBase implements ExtractInfoHelper {
return myInitialInfo.getStatements();
}
@Override
@NotNull
public GrMemberOwner getOwner() {
return myInitialInfo.getOwner();
}
public boolean hasReturnValue() {
return myInitialInfo.hasReturnValue();
}
@@ -16,46 +16,33 @@
package org.jetbrains.plugins.groovy.refactoring.extract;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.method.ExtractMethodInfoHelper;
import org.jetbrains.plugins.groovy.refactoring.inline.GroovyInlineMethodUtil;
import java.util.*;
@@ -63,44 +50,45 @@ import java.util.*;
* @author ilyas
*/
public class ExtractUtil {
private static final Logger LOG = Logger.getInstance(ExtractUtil.class);
private ExtractUtil() {
}
@Nullable
public static PsiElement calculateAnchorToInsertBefore(GrMemberOwner owner, PsiElement startElement) {
while (startElement != null && !isEnclosingDefinition(owner, startElement)) {
if (startElement.getParent() instanceof GroovyFile) {
return startElement.getNextSibling();
}
startElement = startElement.getParent();
PsiElement parent = startElement.getParent();
if (parent instanceof GroovyFile && ((GroovyFile) parent).getScriptClass() == owner) {
return startElement.getNextSibling();
public static GrStatement replaceStatement(GrStatementOwner declarationOwner, ExtractInfoHelper helper) {
GrStatement realStatement;
if (declarationOwner != null && !isSingleExpression(helper.getStatements())) {
// Replace set of statements
final GrStatement[] newStatement = createResultStatement(helper);
// add call statement
final GrStatement[] statements = helper.getStatements();
LOG.assertTrue(statements.length > 0);
realStatement = null;
for (GrStatement statement : newStatement) {
realStatement = declarationOwner.addStatementBefore(statement, statements[0]);
}
LOG.assertTrue(realStatement != null);
// remove old statements
removeOldStatements(declarationOwner, helper);
PsiImplUtil.removeNewLineAfter(realStatement);
}
return startElement == null ? null : startElement.getNextSibling();
}
private static boolean isEnclosingDefinition(GrMemberOwner owner, PsiElement startElement) {
if (owner instanceof GrTypeDefinition) {
GrTypeDefinition definition = (GrTypeDefinition) owner;
return startElement.getParent() == definition.getBody();
else {
// Expression call replace
GrExpression methodCall = createMethodCall(helper);
GrExpression oldExpr = (GrExpression)helper.getStatements()[0];
realStatement = oldExpr.replaceWithExpression(methodCall, true);
}
return false;
}
public enum MethodAccessQualifier {
PUBLIC, PRIVATE, PROTECTED
GrReferenceAdjuster.shortenReferences(realStatement);
return realStatement;
}
@NotNull
public static GrStatement[] createResultStatement(ExtractMethodInfoHelper helper, @NotNull String methodName) {
VariableInfo[] outputVars = helper.getOutputNames();
private static GrStatement[] createResultStatement(ExtractInfoHelper helper) {
VariableInfo[] outputVars = helper.getOutputVariableInfos();
PsiType type = helper.getOutputType();
GrStatement[] statements = helper.getStatements();
GrMethodCallExpression callExpression = createMethodCallByHelper(methodName, helper);
GrMethodCallExpression callExpression = createMethodCall(helper);
if ((outputVars.length == 0 || PsiType.VOID.equals(type)) && !helper.hasReturnValue()) return new GrStatement[]{callExpression};
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject());
@@ -108,7 +96,7 @@ public class ExtractUtil {
return new GrStatement[]{factory.createStatementFromText("return " + callExpression.getText())};
}
assert outputVars.length > 0;
LOG.assertTrue(outputVars.length > 0);
final List<VariableInfo> mustAdd = mustAddVariableDeclaration(statements, outputVars);
if (mustAdd.size() == 0) {
@@ -158,7 +146,7 @@ public class ExtractUtil {
return diffTypes.size() > 1;
}
private static GrStatement createAssignment(VariableInfo[] infos, GrMethodCallExpression callExpression, ExtractMethodInfoHelper helper) {
private static GrStatement createAssignment(VariableInfo[] infos, GrMethodCallExpression callExpression, ExtractInfoHelper helper) {
StringBuilder text = new StringBuilder();
if (infos.length > 1) text.append('(');
for (VariableInfo info : infos) {
@@ -174,46 +162,7 @@ public class ExtractUtil {
return GroovyPsiElementFactory.getInstance(helper.getProject()).createExpressionFromText(text.toString());
}
public static boolean validateMethod(GrMethod method, ExtractMethodInfoHelper helper) {
ArrayList<String> conflicts = new ArrayList<String>();
GrMemberOwner owner = helper.getOwner();
PsiMethod[] methods = ArrayUtil.mergeArrays(owner.getAllMethods(), new PsiMethod[]{method}, PsiMethod.ARRAY_FACTORY);
final Map<PsiMethod, List<PsiMethod>> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy<PsiMethod>() {
public int computeHashCode(PsiMethod method) {
return method.getSignature(PsiSubstitutor.EMPTY).hashCode();
}
public boolean equals(PsiMethod method1, PsiMethod method2) {
return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
}
});
List<PsiMethod> list = map.get(method);
if (list == null) return true;
for (PsiMethod psiMethod : list) {
if (psiMethod != method) {
PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return true;
String message = containingClass instanceof GroovyScriptClass ?
GroovyRefactoringBundle.message("method.is.already.defined.in.script", GroovyRefactoringUtil.getMethodSignature(method),
CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())) :
GroovyRefactoringBundle.message("method.is.already.defined.in.class", GroovyRefactoringUtil.getMethodSignature(method),
CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName()));
conflicts.add(message);
}
}
return conflicts.size() <= 0 || reportConflicts(conflicts, helper.getProject());
}
static boolean reportConflicts(final ArrayList<String> conflicts, final Project project) {
ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts);
conflictsDialog.show();
return conflictsDialog.isOK();
}
public static void removeOldStatements(GrStatementOwner owner, ExtractMethodInfoHelper helper) throws IncorrectOperationException {
private static void removeOldStatements(GrStatementOwner owner, ExtractInfoHelper helper) throws IncorrectOperationException {
owner.removeElements(helper.getInnerElements());
}
@@ -247,49 +196,35 @@ public class ExtractUtil {
return result;
}
private static boolean containVariableDeclaration(@NotNull GrStatement[] statements, @NotNull String varName) {
GroovyPsiElement element = ResolveUtil.resolveProperty(statements[0], varName);
if (element == null) return false;
for (GrStatement statement : statements) {
if (statement.getTextRange().contains(element.getTextRange())) {
return true;
}
}
return false;
}
private static Collection<GrVariable> collectUsedLocalVarsOrParamsDeclaredOutside(GrStatement[] statements) {
final Collection<GrVariable> result = new HashSet<GrVariable>();
public static void renameParameterOccurrences(GrMethod method, ExtractMethodInfoHelper helper) throws IncorrectOperationException {
GrOpenBlock block = method.getBlock();
if (block == null) return;
GrStatement[] statements = block.getStatements();
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject());
for (ParameterInfo info : helper.getParameterInfos()) {
final String oldName = info.getOldName();
final String newName = info.getName();
final ArrayList<GrExpression> result = new ArrayList<GrExpression>();
if (!oldName.equals(newName)) {
for (final GrStatement statement : statements) {
statement.accept(new PsiRecursiveElementVisitor() {
public void visitElement(final PsiElement element) {
super.visitElement(element);
if (element instanceof GrReferenceExpression) {
GrReferenceExpression expr = (GrReferenceExpression) element;
if (!expr.isQualified() && oldName.equals(expr.getName())) {
result.add(expr);
}
}
}
});
for (GrExpression expr : result) {
expr.replaceWithExpression(factory.createExpressionFromText(newName), false);
final int start = statements[0].getTextRange().getStartOffset();
final int end = statements[statements.length - 1].getTextRange().getEndOffset();
final GroovyRecursiveElementVisitor visitor = new GroovyRecursiveElementVisitor() {
@Override
public void visitReferenceExpression(GrReferenceExpression ref) {
final PsiElement resolved = ref.resolve();
if ((resolved instanceof GrParameter ||
resolved instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)resolved)) && resolved.isPhysical()) {
final int offset = resolved.getTextRange().getStartOffset();
//var is declared outside of selected code
if (offset < start || end <= offset) {
result.add((GrVariable)resolved);
}
}
}
};
for (GrStatement statement : statements) {
statement.accept(visitor);
}
return result;
}
public static GrMethod createMethodByHelper(@NotNull String name, ExtractMethodInfoHelper helper) {
public static GrMethod createMethod(ExtractMethodInfoHelper helper) {
StringBuilder buffer = new StringBuilder();
//Add signature
@@ -300,7 +235,7 @@ public class ExtractUtil {
String typeText = getTypeString(helper, false, modifier);
buffer.append(modifier);
buffer.append(typeText);
buffer.append(name);
buffer.append(helper.getName());
buffer.append("(");
for (String param : getParameterString(helper, true)) {
buffer.append(param);
@@ -308,27 +243,47 @@ public class ExtractUtil {
buffer.append(") { \n");
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject());
VariableInfo[] outputInfos = helper.getOutputNames();
generateBody(helper, type == PsiType.VOID, buffer);
buffer.append("\n}");
String methodText = buffer.toString();
GrMethod method = factory.createMethodFromText(methodText);
LOG.assertTrue(method != null);
return method;
}
public static void generateBody(ExtractInfoHelper helper, boolean isVoid, StringBuilder buffer) {
VariableInfo[] outputInfos = helper.getOutputVariableInfos();
ParameterInfo[] infos = helper.getParameterInfos();
boolean[] outputIsParameter = new boolean[outputInfos.length];
for (int i = 0; i < outputInfos.length; i++) {
for (ParameterInfo info : infos) {
if (outputInfos[i].getName().equals(info.getOldName())) {
outputIsParameter[i] = true;
}
}
Set<String> declaredVars = new HashSet<String>();
for (ParameterInfo info : infos) {
declaredVars.add(info.getName());
}
final Set<VariableInfo> mustAdd = new HashSet<VariableInfo>(mustAddVariableDeclaration(helper.getStatements(), outputInfos));
for (VariableInfo info : mustAddVariableDeclaration(helper.getStatements(), outputInfos)) {
declaredVars.add(info.getName());
}
List<VariableInfo> genDecl = new ArrayList<VariableInfo>();
for (int i = 0; i < outputInfos.length; i++) {
VariableInfo info = outputInfos[i];
if (type != PsiType.VOID && !outputIsParameter[i] &&
!mustAdd.contains(info) &&
!containVariableDeclaration(helper.getStatements(), info.getName())) {
genDecl.add(info);
final Collection<GrVariable> outside = collectUsedLocalVarsOrParamsDeclaredOutside(helper.getStatements());
for (final GrVariable variable : outside) {
if (!declaredVars.contains(variable.getName())) {
genDecl.add(new VariableInfo() {
@NotNull
@Override
public String getName() {
return variable.getName();
}
@Override
public PsiType getType() {
return variable.getDeclaredType();
}
});
}
}
final List<GrStatement> statements = generateVarDeclarations(genDecl, helper.getProject(), null);
@@ -341,7 +296,7 @@ public class ExtractUtil {
buffer.append(element.getText());
}
//append return statement
if (type != PsiType.VOID && outputInfos.length > 0) {
if (!isVoid && outputInfos.length > 0) {
buffer.append("\n return ");
if (outputInfos.length > 1) buffer.append('[');
for (VariableInfo info : outputInfos) {
@@ -353,15 +308,29 @@ public class ExtractUtil {
}
else {
GrExpression expr = (GrExpression)PsiUtil.skipParentheses(helper.getStatements()[0], false);
buffer.append(PsiType.VOID.equals(type) ? "" : "return ").append(expr != null ? expr.getText() : "");
boolean addReturn = !isVoid && expr != null && expr.getType() != null && expr.getType() != PsiType.VOID;
if (addReturn) {
buffer.append("return ");
if (expr instanceof GrApplicationStatement) {
final GrApplicationStatement appStatement = (GrApplicationStatement)expr;
buffer.append(appStatement.getInvokedExpression().getText());
buffer.append('(');
final GrCommandArgumentList argList = appStatement.getArgumentList();
if (argList != null) {
buffer.append(argList.getText());
}
buffer.append(')');
}
else {
buffer.append(expr.getText());
}
}
else {
buffer.append(expr != null ? expr.getText() : "");
}
}
buffer.append("\n}");
String methodText = buffer.toString();
GrMethod method = factory.createMethodFromText(methodText);
assert method != null;
return method;
}
public static String[] getParameterString(ExtractInfoHelper helper, boolean useCanonicalText) {
@@ -412,56 +381,14 @@ public class ExtractUtil {
return typeText;
}
public static GrStatement[] getStatementsByElements(PsiElement[] elements) {
ArrayList<GrStatement> statementList = new ArrayList<GrStatement>();
for (PsiElement element : elements) {
if (element instanceof GrStatement) {
statementList.add(((GrStatement) element));
}
}
return statementList.toArray(new GrStatement[statementList.size()]);
}
public static PsiElement[] getElementsInOffset(PsiFile file, int startOffset, int endOffset) {
PsiElement[] elements;
GrExpression expr = GroovyRefactoringUtil.findElementInRange(file, startOffset, endOffset, GrExpression.class);
if (expr != null) {
PsiElement parent = expr.getParent();
if (expr.getParent() instanceof GrMethodCallExpression || parent instanceof GrIndexProperty) {
expr = ((GrExpression) expr.getParent());
}
elements = new PsiElement[]{expr};
} else {
elements = GroovyRefactoringUtil.findStatementsInRange(file, startOffset, endOffset, true);
}
return elements;
}
@Nullable
public static GrMemberOwner getMemberOwner(GrStatement statement) {
PsiElement parent = statement.getParent();
while (parent != null && !(parent instanceof GrMemberOwner)) {
if (parent instanceof GroovyFileBase) return (GrMemberOwner) ((GroovyFileBase) parent).getScriptClass();
parent = parent.getParent();
}
return parent != null ? ((GrMemberOwner) parent) : null;
}
@Nullable
public static GrStatementOwner getDeclarationOwner(GrStatement statement) {
PsiElement parent = statement.getParent();
return parent instanceof GrStatementOwner ? ((GrStatementOwner) parent) : null;
}
public static boolean isSingleExpression(GrStatement[] statements) {
return statements.length == 1 && statements[0] instanceof GrExpression &&
!(statements[0].getParent() instanceof GrVariableDeclarationOwner && statements[0] instanceof GrAssignmentExpression);
}
public static GrMethodCallExpression createMethodCallByHelper(@NotNull String name, ExtractMethodInfoHelper helper) {
private static GrMethodCallExpression createMethodCall(ExtractInfoHelper helper) {
StringBuilder buffer = new StringBuilder();
buffer.append(name).append("(");
buffer.append(helper.getName()).append("(");
int number = 0;
for (ParameterInfo info : helper.getParameterInfos()) {
if (info.passAsParameter()) number++;
@@ -482,7 +409,7 @@ public class ExtractUtil {
String callText = buffer.toString();
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject());
GrExpression expr = factory.createExpressionFromText(callText);
assert expr instanceof GrMethodCallExpression;
LOG.assertTrue(expr instanceof GrMethodCallExpression, callText);
return ((GrMethodCallExpression) expr);
}
@@ -505,35 +432,10 @@ public class ExtractUtil {
return statement.getTextOffset();
}
public static boolean canBeStatic(GrStatement statement) {
PsiElement parent = statement.getParent();
while (parent != null && !(parent instanceof PsiFile)) {
if (parent instanceof GrMethod) {
return ((GrMethod) parent).hasModifierProperty(PsiModifier.STATIC);
}
parent = parent.getParent();
}
return false;
}
public static String getModifierString(ExtractMethodInfoHelper helper) {
String visibility = helper.getVisibility();
assert visibility != null && visibility.length() > 0;
LOG.assertTrue(visibility != null && visibility.length() > 0);
visibility = visibility.equals(PsiModifier.PUBLIC) ? "" : visibility + " ";
return visibility + (helper.isStatic() ? "static " : "");
}
public static boolean isReturnStatement(GrStatement statement, Collection<GrStatement> returnStatements) {
if (statement instanceof GrReturnStatement) return true;
if (statement instanceof GrIfStatement) {
boolean checked = GroovyInlineMethodUtil.checkTailIfStatement(((GrIfStatement) statement), returnStatements);
return checked & returnStatements.size() == 0;
}
if (statement instanceof GrExpression) {
return returnStatements.contains(statement);
}
return false;
}
}
@@ -42,7 +42,6 @@ public class InitialInfo implements ExtractInfoHelper {
private final ParameterInfo[] myParameterInfos;
private final VariableInfo[] myOutputNames;
private final PsiType myOutputType;
private final GrMemberOwner myTargetClass;
private final PsiElement[] myInnerElements;
private final Project myProject;
private final GrStatement[] myStatements;
@@ -57,7 +56,6 @@ public class InitialInfo implements ExtractInfoHelper {
ArrayList<GrStatement> returnStatements) {
myInnerElements = innerElements;
myStatements = statements;
myTargetClass = targetClass;
myOutputNames = outputInfos;
myHasReturnValue = ContainerUtil.find(returnStatements, new Condition<GrStatement>() {
@@ -94,7 +92,7 @@ public class InitialInfo implements ExtractInfoHelper {
outputType = outputInfos[0].getType();
}
else {
outputType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, myTargetClass);
outputType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, myStatements[0]);
}
}
else if (ExtractUtil.isSingleExpression(statements)) {
@@ -137,7 +135,7 @@ public class InitialInfo implements ExtractInfoHelper {
@Override
@NotNull
public VariableInfo[] getOutputNames() {
public VariableInfo[] getOutputVariableInfos() {
return myOutputNames;
}
@@ -170,13 +168,12 @@ public class InitialInfo implements ExtractInfoHelper {
return myStatements;
}
@Override
@NotNull
public GrMemberOwner getOwner() {
return myTargetClass;
}
public boolean hasReturnValue() {
return myHasReturnValue;
}
@Override
public String getName() {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.actions.BasePlatformRefactoringAction;
import org.jetbrains.annotations.NotNull;
/**
* @author Max Medvedev
*/
public class ExtractClosureAction extends BasePlatformRefactoringAction {
@Override
protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) {
return new ExtractClosureHandler();
}
@Override
protected boolean isAvailableInEditorOnly() {
return true;
}
}
@@ -0,0 +1,263 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import gnu.trove.TIntArrayList;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import org.jetbrains.plugins.groovy.GroovyFileType;
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.params.GrParameter;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel;
import org.jetbrains.plugins.groovy.refactoring.ui.GrMethodSignatureComponent;
import javax.swing.*;
import java.awt.*;
/**
* @author Max Medvedev
*/
public class ExtractClosureDialog extends DialogWrapper {
private final ParameterTablePanel myTable;
private final GrMethodSignatureComponent mySignature;
private final EditorTextField myNameField;
private final ExtractClosureHelper myHelper;
private final JBCheckBox myFinalCB;
private final JBCheckBox myGenerateDelegateCB;
TObjectIntHashMap<JBCheckBox> toRemoveCBs;
private final InitialInfo myInitialInfo;
public ExtractClosureDialog(InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) {
super(initialInfo.getProject());
myInitialInfo = initialInfo;
myHelper = new ExtractClosureHelper(initialInfo, owner, toSearchFor, "", false);
setTitle(ExtractClosureHandler.EXTRACT_CLOSURE);
myTable = new ParameterTablePanel() {
@Override
protected void updateSignature() {
ExtractClosureDialog.this.updateSignature();
}
@Override
protected void doEnterAction() {
clickDefaultButton();
}
@Override
protected void doCancelAction() {
ExtractClosureDialog.this.doCancelAction();
}
};
mySignature = new GrMethodSignatureComponent("", initialInfo.getProject());
myNameField = new EditorTextField("", initialInfo.getProject(), GroovyFileType.GROOVY_FILE_TYPE);
myFinalCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("Declare &final"));
myFinalCB.setFocusable(false);
myGenerateDelegateCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("De&legate via overloading method"));
myGenerateDelegateCB.setFocusable(false);
TObjectIntHashMap<GrParameter> parametersToRemove = findParametersToRemove(myHelper);
toRemoveCBs = new TObjectIntHashMap<JBCheckBox>(parametersToRemove.size());
for (Object p : parametersToRemove.keys()) {
JBCheckBox cb = new JBCheckBox(GroovyRefactoringBundle.message("remove.parameter.0.no.longer.used", ((GrParameter)p).getName()));
cb.setFocusable(false);
cb.setSelected(false);
toRemoveCBs.put(cb, parametersToRemove.get((GrParameter)p));
}
init();
}
@Override
protected ValidationInfo doValidate() {
final String text = myNameField.getText().trim();
if (!StringUtil.isJavaIdentifier(text)) {
return new ValidationInfo(GroovyRefactoringBundle.message("name.is.wrong", text), myNameField);
}
final Ref<ValidationInfo> info = new Ref<ValidationInfo>();
toRemoveCBs.forEachEntry(new TObjectIntProcedure<JBCheckBox>() {
@Override
public boolean execute(JBCheckBox checkbox, int index) {
if (!checkbox.isSelected()) return true;
final ParameterInfo param = myHelper.getParameterInfos()[index];
if (!param.passAsParameter()) return true;
final ParameterInfo initialParam = myInitialInfo.getParameterInfos()[index];
final String message = GroovyRefactoringBundle.message("you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method",
param.getName(), initialParam.getName());
info.set(new ValidationInfo(message));
return false;
}
});
return info.get();
}
private void updateSignature() {
StringBuilder b = new StringBuilder();
b.append("{ ");
String[] params = ExtractUtil.getParameterString(myHelper, false);
for (int i = 0; i < params.length; i++) {
if (i > 0) {
b.append(" ");
}
b.append(params[i]);
b.append('\n');
}
b.append(" ->\n}");
mySignature.setSignature(b.toString());
}
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
@Override
protected void init() {
super.init();
myTable.init(myHelper);
final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
final Boolean settingsFinals = refactoringSettings.INTRODUCE_PARAMETER_CREATE_FINALS;
myFinalCB.setSelected(settingsFinals == null ?
CodeStyleSettingsManager.getSettings(myHelper.getProject()).GENERATE_FINAL_PARAMETERS :
settingsFinals.booleanValue());
myGenerateDelegateCB.setSelected(false);
updateSignature();
}
@Override
protected JComponent createNorthPanel() {
final JPanel panel = new JPanel(new BorderLayout());
final JBLabel nameLabel = new JBLabel();
panel.add(nameLabel, BorderLayout.NORTH);
panel.add(myNameField, BorderLayout.CENTER);
nameLabel.setText(UIUtil.replaceMnemonicAmpersand("Parameter &name:"));
nameLabel.setLabelFor(myNameField);
final JPanel checkBoxPanel = new JPanel();
checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.Y_AXIS));
checkBoxPanel.add(myFinalCB);
checkBoxPanel.add(myGenerateDelegateCB);
for (Object cb : toRemoveCBs.keys()) {
checkBoxPanel.add((Component)cb);
}
panel.add(checkBoxPanel, BorderLayout.SOUTH);
return panel;
}
@Override
protected JComponent createCenterPanel() {
final JPanel signaturePanel = new JPanel(new BorderLayout());
signaturePanel.setBorder(
IdeBorderFactory.createTitledBorder(GroovyRefactoringBundle.message("signature.preview.border.title"), false, false, true));
signaturePanel.add(mySignature, BorderLayout.CENTER);
Splitter splitter = new Splitter(true);
splitter.setFirstComponent(myTable);
splitter.setSecondComponent(signaturePanel);
mySignature.setPreferredSize(new Dimension(500, 100));
mySignature.setSize(new Dimension(500, 100));
splitter.setShowDividerIcon(false);
return splitter;
}
@Override
protected void doOKAction() {
final JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
settings.INTRODUCE_PARAMETER_CREATE_FINALS = Boolean.valueOf(myFinalCB.isSelected());
myHelper.setName(myNameField.getText());
myHelper.setDeclareFinal(myFinalCB.isSelected());
myHelper.setGenerateDelegate(myGenerateDelegateCB.isSelected());
TIntArrayList list = new TIntArrayList();
for (Object cb : toRemoveCBs.keys()) {
final JBCheckBox checkbox = (JBCheckBox)cb;
if (checkbox.isSelected()) {
list.add(toRemoveCBs.get(checkbox));
}
}
myHelper.setToRemove(list);
super.doOKAction();
}
public ExtractClosureHelper getHelper() {
return myHelper;
}
private static TObjectIntHashMap<GrParameter> findParametersToRemove(ExtractClosureHelper helper) {
final TObjectIntHashMap<GrParameter> result = new TObjectIntHashMap<GrParameter>();
final GrStatement[] statements = helper.getStatements();
final int start = statements[0].getTextRange().getStartOffset();
final int end = statements[statements.length - 1].getTextRange().getEndOffset();
GrParameter[] parameters = helper.getOwner().getParameters();
for (int i = 0; i < parameters.length; i++) {
GrParameter parameter = parameters[i];
if (shouldRemove(parameter, start, end)) {
result.put(parameter, i);
}
}
return result;
}
private static boolean shouldRemove(GrParameter parameter, int start, int end) {
for (PsiReference reference : ReferencesSearch.search(parameter)) {
final PsiElement element = reference.getElement();
if (element == null) continue;
final int offset = element.getTextRange().getStartOffset();
if (offset < start || end <= offset) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.refactoring.introduceParameter.ExternalUsageInfo;
import com.intellij.usageView.UsageInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.ArrayList;
import java.util.List;
/**
* @author Max Medvedev
*/
public class ExtractClosureFromClosureProcessor extends ExtractClosureProcessorBase {
public ExtractClosureFromClosureProcessor(@NotNull ExtractClosureHelper helper) {
super(helper);
}
@Override
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
//todo
return true;
}
@Override
protected void performRefactoring(UsageInfo[] usages) {
//To change body of implemented methods use File | Settings | File Templates. todo
}
@NotNull
@Override
protected UsageInfo[] findUsages() {
final List<UsageInfo> result = new ArrayList<UsageInfo>();
final GrVariable var = (GrVariable)myHelper.getToSearchFor();
for (PsiReference ref : ReferencesSearch.search(var, GlobalSearchScope.allScope(myHelper.getProject()), true)) {
final PsiElement element = ref.getElement();
if (element.getLanguage() != GroovyFileType.GROOVY_LANGUAGE) {
result.add(new OtherLanguageUsageInfo(ref));
continue;
}
final GrCall call = GroovyRefactoringUtil.getCallExpressionByMethodReference(element);
if (call == null) continue;
result.add(new ExternalUsageInfo(element));
}
return result.toArray(new UsageInfo[result.size()]);
}
}
@@ -0,0 +1,270 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.IntroduceParameterRefactoring;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduceParameter.ChangedMethodCallInfo;
import com.intellij.refactoring.introduceParameter.ExternalUsageInfo;
import com.intellij.refactoring.introduceParameter.InternalUsageInfo;
import com.intellij.refactoring.introduceParameter.IntroduceParameterData;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
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.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.AnySupers;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.FieldConflictsResolver;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrExpressionWrapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static com.intellij.refactoring.introduceParameter.IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts;
import static com.intellij.refactoring.introduceParameter.IntroduceParameterUtil.processUsages;
import static org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GroovyIntroduceParameterUtil.*;
/**
* @author Max Medvedev
*/
public class ExtractClosureFromMethodProcessor extends ExtractClosureProcessorBase {
private final GrMethod myMethod;
private Editor myEditor;
public ExtractClosureFromMethodProcessor(@NotNull ExtractClosureHelper helper, Editor editor) {
super(helper);
myEditor = editor;
myMethod = (GrMethod)myHelper.getOwner();
}
@Override
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
UsageInfo[] usagesIn = refUsages.get();
MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
final GrStatement[] statements = myHelper.getStatements();
for (GrStatement statement : statements) {
detectAccessibilityConflicts(statement, usagesIn, conflicts, false, myProject);
}
for (UsageInfo info : usagesIn) {
if (info instanceof OtherLanguageUsageInfo) {
conflicts.putValue(info.getElement(), GroovyRefactoringBundle.message("cannot.process.usage.in.language.{0}", CommonRefactoringUtil.htmlEmphasize(info.getElement().getLanguage().getDisplayName())));
}
}
if (!myMethod.hasModifierProperty(PsiModifier.PRIVATE)) {
final AnySupers anySupers = new AnySupers();
for (GrStatement statement : statements) {
statement.accept(anySupers);
}
if (anySupers.isResult()) {
for (UsageInfo usageInfo : usagesIn) {
if (!(usageInfo.getElement() instanceof PsiMethod) && !(usageInfo instanceof InternalUsageInfo)) {
if (!PsiTreeUtil.isAncestor(myMethod.getContainingClass(), usageInfo.getElement(), false)) {
conflicts.putValue(statements[0], RefactoringBundle
.message("parameter.initializer.contains.0.but.not.all.calls.to.method.are.in.its.class",
CommonRefactoringUtil.htmlEmphasize(PsiKeyword.SUPER)));
break;
}
}
}
}
}
return showConflicts(conflicts, usagesIn);
}
@NotNull
@Override
protected UsageInfo[] findUsages() {
List<UsageInfo> result = new ArrayList<UsageInfo>();
final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor();
for (PsiReference ref1 : MethodReferencesSearch.search(toSearchFor, GlobalSearchScope.projectScope(myProject), true)) {
PsiElement ref = ref1.getElement();
if (ref.getLanguage() != GroovyFileType.GROOVY_LANGUAGE) {
result.add(new OtherLanguageUsageInfo(ref1));
continue;
}
if (ref instanceof PsiMethod && ((PsiMethod)ref).isConstructor()) {
DefaultConstructorImplicitUsageInfo implicitUsageInfo =
new DefaultConstructorImplicitUsageInfo((PsiMethod)ref, ((PsiMethod)ref).getContainingClass(), toSearchFor);
result.add(implicitUsageInfo);
}
else if (ref instanceof PsiClass) {
result.add(new NoConstructorClassUsageInfo((PsiClass)ref));
}
else if (!PsiTreeUtil.isAncestor(myMethod, ref, false)) {
result.add(new ExternalUsageInfo(ref));
}
else {
result.add(new ChangedMethodCallInfo(ref));
}
}
Collection<PsiMethod> overridingMethods = OverridingMethodsSearch.search(toSearchFor, true).findAll();
for (PsiMethod overridingMethod : overridingMethods) {
result.add(new UsageInfo(overridingMethod));
}
final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]);
return UsageViewUtil.removeDuplicatedUsages(usageInfos);
}
@Override
protected void performRefactoring(UsageInfo[] usages) {
final IntroduceParameterData data = new IntroduceParameterDataAdapter();
processUsages(usages, data);
final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor();
final boolean methodsToProcessAreDifferent = myMethod != toSearchFor;
if (myHelper.generateDelegate()) {
generateDelegate(myMethod, data.getParameterInitializer(), myProject);
if (methodsToProcessAreDifferent) {
final GrMethod method = generateDelegate(toSearchFor, data.getParameterInitializer(), myProject);
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
final GrOpenBlock block = method.getBlock();
if (block != null) {
block.delete();
}
}
}
}
// Changing signature of initial method
// (signature of myMethodToReplaceIn will be either changed now or have already been changed)
final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(myHelper.getName(), myMethod.getBlock());
changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethod), usages, data);
if (methodsToProcessAreDifferent) {
changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(toSearchFor), usages, data);
}
// Replacing expression occurrences
for (UsageInfo usage : usages) {
if (usage instanceof ChangedMethodCallInfo) {
PsiElement element = usage.getElement();
processChangedMethodCall(element, myHelper, myProject);
}
}
final GrStatement newStatement = ExtractUtil.replaceStatement(myMethod.getBlock(), myHelper);
if (myEditor != null) {
PsiDocumentManager.getInstance(myProject).commitDocument(myEditor.getDocument());
myEditor.getCaretModel().moveToOffset(ExtractUtil.getCaretOffset(newStatement));
}
fieldConflictsResolver.fix();
}
private class IntroduceParameterDataAdapter implements IntroduceParameterData {
private final GrClosableBlock myClosure;
private final GrExpressionWrapper myWrapper;
private final PsiType myType;
private IntroduceParameterDataAdapter() {
myClosure = generateClosure();
myWrapper = new GrExpressionWrapper(myClosure);
myType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, myMethod);
}
@NotNull
@Override
public Project getProject() {
return myProject;
}
@Override
public PsiMethod getMethodToReplaceIn() {
return myMethod;
}
@NotNull
@Override
public PsiMethod getMethodToSearchFor() {
return (PsiMethod)myHelper.getToSearchFor();
}
@Override
public ExpressionWrapper getParameterInitializer() {
return myWrapper;
}
@NotNull
@Override
public String getParameterName() {
return myHelper.getName();
}
@Override
public int getReplaceFieldsWithGetters() {
return IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE; //todo add option to dialog
}
@Override
public boolean isDeclareFinal() {
return myHelper.declareFinal();
}
@Override
public boolean isGenerateDelegate() {
return false; //todo
}
@NotNull
@Override
public PsiType getForcedType() {
return myType;
}
@NotNull
@Override
public TIntArrayList getParametersToRemove() {
return myHelper.parametersToRemove();
}
}
}
@@ -0,0 +1,173 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.ide.util.SuperMethodWarningUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.IntroduceTargetChooser;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.Function;
import com.intellij.util.PairFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.HelpID;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractException;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractHandlerBase;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase;
import org.jetbrains.plugins.groovy.refactoring.ui.MethodOrClosureScopeChooser;
import java.util.ArrayList;
import java.util.List;
/**
* @author Max Medvedev
*/
public class ExtractClosureHandler extends ExtractHandlerBase implements RefactoringActionHandler, MethodOrClosureScopeChooser.JBPopupOwner {
public static final String EXTRACT_CLOSURE = "Extract Closure";
private JBPopup myPopup;
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) {
final SelectionModel selectionModel = editor.getSelectionModel();
if (!selectionModel.hasSelection()) {
final int offset = editor.getCaretModel().getOffset();
final List<GrExpression> expressions = GrIntroduceHandlerBase.collectExpressions(file, editor, offset);
if (expressions.size() == 1) {
final TextRange textRange = expressions.get(0).getTextRange();
selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset());
}
else {
final Pass<GrExpression> callback = new Pass<GrExpression>() {
public void pass(final GrExpression selectedValue) {
invoke(project, editor, file, selectedValue.getTextRange().getStartOffset(), selectedValue.getTextRange().getEndOffset());
}
};
final Function<GrExpression, String> renderer = new Function<GrExpression, String>() {
@Override
public String fun(GrExpression grExpression) {
return grExpression.getText();
}
};
IntroduceTargetChooser.showChooser(editor, expressions, callback, renderer);
return;
}
}
invoke(project, editor, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
}
void invoke(Project project, Editor editor, PsiFile file, int start, int end) {
try {
invokeOnEditor(project, editor, file, start, end);
}
catch (ExtractException e) {
CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), EXTRACT_CLOSURE, HelpID.GROOVY_EXTRACT_CLOSURE);
}
}
@Nullable
protected ExtractClosureHelper getSettings(@NotNull InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) {
final ExtractClosureDialog dialog = new ExtractClosureDialog(initialInfo, owner, toSearchFor);
dialog.show();
if (!dialog.isOK()) return null;
return dialog.getHelper();
}
private void findScope(@NotNull PsiElement place,
@NotNull final Editor editor,
@NotNull PairFunction<GrParametersOwner, PsiElement, Object> callback) {
final List<GrParametersOwner> scopes = new ArrayList<GrParametersOwner>();
while (true) {
final GrParametersOwner parent = PsiTreeUtil.getParentOfType(place, GrMethod.class, GrClosableBlock.class);
if (parent == null) break;
scopes.add(parent);
place = parent;
}
if (scopes.size() == 0) {
throw new ExtractException(GroovyRefactoringBundle.message("there.is.no.method.or.closure"));
}
else if (scopes.size() == 1) {
final GrParametersOwner owner = scopes.get(0);
if (owner instanceof GrMethod) {
PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod((PsiMethod)owner, RefactoringBundle.message("to.refactor"));
if (newMethod == null) return;
callback.fun(owner, newMethod);
}
else {
callback.fun(owner, MethodOrClosureScopeChooser.findVariableToUse(owner));
}
}
else {
myPopup = MethodOrClosureScopeChooser.create(scopes, editor, this, callback);
myPopup.showInBestPositionFor(editor);
}
}
@Override
public void performRefactoring(@NotNull final InitialInfo info,
@NotNull GrMemberOwner owner,
GrStatementOwner declarationOwner,
final Editor editor,
PsiElement startElement) {
findScope(startElement, editor, new PairFunction<GrParametersOwner, PsiElement, Object>() {
@Override
public Object fun(GrParametersOwner owner, PsiElement toSearchFor) {
final ExtractClosureHelper helper = getSettings(info, owner, toSearchFor);
if (helper == null) return null;
if (helper.getOwner() instanceof GrMethod) {
new ExtractClosureFromMethodProcessor(helper, editor).run();
}
return null;
}
});
}
@Override
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
//do nothing
}
@Override
public JBPopup get() {
return myPopup;
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import gnu.trove.TIntArrayList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelper;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelperBase;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterSettings;
/**
* @author Max Medvedev
*/
public class ExtractClosureHelper extends ExtractInfoHelperBase implements ExtractInfoHelper, GrIntroduceParameterSettings {
private final GrParametersOwner myOwner;
private final PsiElement myToSearchFor;
private String myName;
private boolean myFinal;
private TIntArrayList toRemove;
private boolean myGenerateDelegate;
public ExtractClosureHelper(InitialInfo info, GrParametersOwner owner, PsiElement toSearchFor, String name, boolean declareFinal) {
super(info);
myOwner = owner;
myToSearchFor = toSearchFor;
myName = name;
myFinal = declareFinal;
}
public GrParametersOwner getOwner() {
return myOwner;
}
public PsiElement getToSearchFor() {
return myToSearchFor;
}
public String getName() {
return myName;
}
public boolean declareFinal() {
return myFinal;
}
public void setName(String name) {
myName = name;
}
public void setDeclareFinal(boolean aFinal) {
myFinal = aFinal;
}
public void setToRemove(TIntArrayList toRemove) {
this.toRemove = toRemove;
}
@Override
public TIntArrayList parametersToRemove() {
return toRemove;
}
@Override
public int replaceFieldsWithGetters() {
return 0;//todo
}
@Override
public boolean removeLocalVariable() {
return false;
}
@Override
public boolean replaceAllOccurrences() {
return false;
}
@Override
public PsiType getSelectedType() {
return JavaPsiFacade.getElementFactory(getProject()).createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, myOwner);
}
public boolean generateDelegate() {
return myGenerateDelegate;
}
public void setGenerateDelegate(boolean generateDelegate) {
myGenerateDelegate = generateDelegate;
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.ui.UsageViewDescriptorAdapter;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
/**
* @author Max Medvedev
*/
public abstract class ExtractClosureProcessorBase extends BaseRefactoringProcessor {
protected final ExtractClosureHelper myHelper;
public ExtractClosureProcessorBase(@NotNull ExtractClosureHelper helper) {
super(helper.getProject());
myHelper = helper;
}
@NotNull
@Override
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new UsageViewDescriptorAdapter() {
@NotNull
@Override
public PsiElement[] getElements() {
return new PsiElement[]{myHelper.getToSearchFor()};
}
@Override
public String getProcessedElementsHeader() {
return ExtractClosureHandler.EXTRACT_CLOSURE;
}
};
}
@Override
protected String getCommandName() {
return ExtractClosureHandler.EXTRACT_CLOSURE;
}
protected GrClosableBlock generateClosure() {
StringBuilder buffer = new StringBuilder();
buffer.append('{');
for (String p : ExtractUtil.getParameterString(myHelper, true)) {
buffer.append(p);
}
buffer.append("->\n");
ExtractUtil.generateBody(myHelper, false, buffer);
buffer.append('}');
return GroovyPsiElementFactory.getInstance(myHelper.getProject()).createClosureFromText(buffer.toString(), myHelper.getOwner());
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.psi.PsiReference;
import com.intellij.usageView.UsageInfo;
import org.jetbrains.annotations.NotNull;
/**
* @author Max Medvedev
*/
public class OtherLanguageUsageInfo extends UsageInfo {
public OtherLanguageUsageInfo(@NotNull PsiReference reference) {
super(reference);
}
}
@@ -16,9 +16,14 @@
package org.jetbrains.plugins.groovy.refactoring.extract.method;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiModifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelperBase;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
/**
@@ -30,14 +35,16 @@ public class ExtractMethodInfoHelper extends ExtractInfoHelperBase {
private boolean mySpecifyType = true;
private String myVisibility;
private String myName;
private final GrMemberOwner myOwner;
public ExtractMethodInfoHelper(InitialInfo initialInfo, String name) {
public ExtractMethodInfoHelper(InitialInfo initialInfo, String name, GrMemberOwner owner) {
super(initialInfo);
myOwner = owner;
myVisibility = PsiModifier.PRIVATE;
myName = name;
myIsStatic = ExtractUtil.canBeStatic(initialInfo.getStatements()[0]);
myIsStatic = canBeStatic(initialInfo.getStatements()[0]);
}
public boolean isStatic() {
@@ -67,4 +74,20 @@ public class ExtractMethodInfoHelper extends ExtractInfoHelperBase {
public void setName(String name) {
myName = name;
}
@NotNull
public GrMemberOwner getOwner() {
return myOwner;
}
private static boolean canBeStatic(GrStatement statement) {
PsiElement parent = statement.getParent();
while (parent != null && !(parent instanceof PsiFile)) {
if (parent instanceof GrMethod) {
return ((GrMethod) parent).hasModifierProperty(PsiModifier.STATIC);
}
parent = parent.getParent();
}
return false;
}
}
@@ -22,19 +22,30 @@ import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.ui.ComboBoxVisibilityPanel;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.ui.MethodSignatureComponent;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.util.ArrayUtil;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel;
@@ -51,7 +62,10 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.Map;
/**
* @author ilyas
@@ -71,10 +85,10 @@ public class GroovyExtractMethodDialog extends DialogWrapper {
private ParameterTablePanel myParameterTablePanel;
private final Project myProject;
public GroovyExtractMethodDialog(InitialInfo info) {
public GroovyExtractMethodDialog(InitialInfo info, GrMemberOwner owner) {
super(info.getProject(), true);
myProject = info.getProject();
myHelper = new ExtractMethodInfoHelper(info, "");
myHelper = new ExtractMethodInfoHelper(info, "", owner);
setUpNameField();
myParameterTablePanel.init(myHelper);
@@ -98,8 +112,8 @@ public class GroovyExtractMethodDialog extends DialogWrapper {
protected void doOKAction() {
String name = getEnteredName();
if (name == null) return;
GrMethod method = ExtractUtil.createMethodByHelper(name, myHelper);
if (method != null && !ExtractUtil.validateMethod(method, myHelper)) {
GrMethod method = ExtractUtil.createMethod(myHelper);
if (method != null && !validateMethod(method, myHelper)) {
return;
}
if (myCbSpecifyType.isEnabled()) {
@@ -232,6 +246,44 @@ public class GroovyExtractMethodDialog extends DialogWrapper {
};
}
private static boolean validateMethod(GrMethod method, ExtractMethodInfoHelper helper) {
ArrayList<String> conflicts = new ArrayList<String>();
GrMemberOwner owner = helper.getOwner();
PsiMethod[] methods = ArrayUtil.mergeArrays(owner.getAllMethods(), new PsiMethod[]{method}, PsiMethod.ARRAY_FACTORY);
final Map<PsiMethod, List<PsiMethod>> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy<PsiMethod>() {
public int computeHashCode(PsiMethod method) {
return method.getSignature(PsiSubstitutor.EMPTY).hashCode();
}
public boolean equals(PsiMethod method1, PsiMethod method2) {
return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY));
}
});
List<PsiMethod> list = map.get(method);
if (list == null) return true;
for (PsiMethod psiMethod : list) {
if (psiMethod != method) {
PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return true;
String message = containingClass instanceof GroovyScriptClass ?
GroovyRefactoringBundle.message("method.is.already.defined.in.script", GroovyRefactoringUtil.getMethodSignature(method),
CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())) :
GroovyRefactoringBundle.message("method.is.already.defined.in.class", GroovyRefactoringUtil.getMethodSignature(method),
CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName()));
conflicts.add(message);
}
}
return conflicts.size() <= 0 || reportConflicts(conflicts, helper.getProject());
}
private static boolean reportConflicts(final ArrayList<String> conflicts, final Project project) {
ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts);
conflictsDialog.show();
return conflictsDialog.isOK();
}
class DataChangedListener implements EventListener {
void dataChanged() {
update();
@@ -17,9 +17,9 @@
package org.jetbrains.plugins.groovy.refactoring.extract.method;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.SelectionModel;
@@ -29,27 +29,29 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractException;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractHandlerBase;
import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil;
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo;
import org.jetbrains.plugins.groovy.refactoring.extract.*;
import java.util.ArrayList;
/**
* @author ilyas
*/
public class GroovyExtractMethodHandler extends ExtractHandlerBase<ExtractMethodInfoHelper> implements RefactoringActionHandler {
private static final Logger LOG = Logger.getInstance(GroovyExtractMethodHandler.class);
public class GroovyExtractMethodHandler extends ExtractHandlerBase implements RefactoringActionHandler {
protected static String REFACTORING_NAME = GroovyRefactoringBundle.message("extract.method.title");
private String myInvokeResult = "ok";
@@ -73,85 +75,60 @@ public class GroovyExtractMethodHandler extends ExtractHandlerBase<ExtractMethod
}
}
public void performRefactoring(@NotNull final ExtractMethodInfoHelper helper,
public void performRefactoring(@NotNull final InitialInfo initialInfo,
@NotNull final GrMemberOwner owner,
final GrStatementOwner declarationOwner,
final Editor editor,
final PsiElement startElement) {
final ExtractMethodInfoHelper helper = getSettings(initialInfo, owner);
if (helper == null) return;
final String methodName = helper.getName();
final GrMethod method = ExtractUtil.createMethodByHelper(methodName, helper);
final Runnable runnable = new Runnable() {
CommandProcessor.getInstance().executeCommand(helper.getProject(), new Runnable() {
public void run() {
final AccessToken lock = ApplicationManager.getApplication().acquireWriteActionLock(GroovyExtractMethodHandler.class);
try {
PsiElement anchor = ExtractUtil.calculateAnchorToInsertBefore(owner, startElement);
GrMethod newMethod = owner.addMemberDeclaration(method, anchor);
ExtractUtil.renameParameterOccurrences(newMethod, helper);
GrReferenceAdjuster.shortenReferences(newMethod);
GrStatement realStatement;
if (declarationOwner != null && !ExtractUtil.isSingleExpression(helper.getStatements())) {
// Replace set of statements
final GrStatement[] newStatement = ExtractUtil.createResultStatement(helper, methodName);
// add call statement
final GrStatement[] statements = helper.getStatements();
assert statements.length > 0;
realStatement = null;
for (GrStatement statement : newStatement) {
realStatement = declarationOwner.addStatementBefore(statement, statements[0]);
}
assert realStatement != null;
// remove old statements
ExtractUtil.removeOldStatements(declarationOwner, helper);
PsiImplUtil.removeNewLineAfter(realStatement);
}
else {
// Expression call replace
GrExpression methodCall = ExtractUtil.createMethodCallByHelper(methodName, helper);
GrExpression oldExpr = (GrExpression)helper.getStatements()[0];
realStatement = oldExpr.replaceWithExpression(methodCall, true);
}
GrReferenceAdjuster.shortenReferences(realStatement);
PsiElement prev = newMethod.getPrevSibling();
IElementType elementType = prev.getNode().getElementType();
if (!TokenSets.WHITE_SPACES_SET.contains(elementType) || !prev.getText().contains("\n")) {
newMethod.getParent().getNode().addLeaf(GroovyTokenTypes.mNLS, "\n", newMethod.getNode());
}
createMethod(helper, owner, startElement);
GrStatement realStatement = ExtractUtil.replaceStatement(declarationOwner, helper);
// move to offset
if (editor != null) {
PsiDocumentManager.getInstance(helper.getProject()).commitDocument(editor.getDocument());
editor.getSelectionModel().removeSelection();
editor.getCaretModel().moveToOffset(ExtractUtil.getCaretOffset(realStatement));
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
finally {
lock.finish();
}
}
};
Project project = helper.getProject();
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(runnable);
editor.getSelectionModel().removeSelection();
}
}, REFACTORING_NAME, null);
}
@Override
public ExtractMethodInfoHelper getSettings(@NotNull InitialInfo initialInfo) {
private static void createMethod(ExtractMethodInfoHelper helper, GrMemberOwner owner, PsiElement startElement) {
final GrMethod method = ExtractUtil.createMethod(helper);
PsiElement anchor = calculateAnchorToInsertBefore(owner, startElement);
GrMethod newMethod = owner.addMemberDeclaration(method, anchor);
renameParameterOccurrences(newMethod, helper);
GrReferenceAdjuster.shortenReferences(newMethod);
PsiElement prev = newMethod.getPrevSibling();
IElementType elementType = prev.getNode().getElementType();
if (!TokenSets.WHITE_SPACES_SET.contains(elementType) || !prev.getText().contains("\n")) {
newMethod.getParent().getNode().addLeaf(GroovyTokenTypes.mNLS, "\n", newMethod.getNode());
}
}
@Nullable
private static ExtractMethodInfoHelper getSettings(@NotNull InitialInfo initialInfo, GrMemberOwner owner) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
final ExtractMethodInfoHelper helper = new ExtractMethodInfoHelper(initialInfo, "testMethod");
final ExtractMethodInfoHelper helper = new ExtractMethodInfoHelper(initialInfo, "testMethod", owner);
final PsiType type = helper.getOutputType();
if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || type.equalsToText("void")) {
if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || PsiType.VOID.equals(type)) {
helper.setSpecifyType(false);
}
return helper;
}
GroovyExtractMethodDialog dialog = new GroovyExtractMethodDialog(initialInfo);
GroovyExtractMethodDialog dialog = new GroovyExtractMethodDialog(initialInfo, owner);
dialog.show();
if (!dialog.isOK()) return null;
@@ -166,4 +143,58 @@ public class GroovyExtractMethodHandler extends ExtractHandlerBase<ExtractMethod
public String getInvokeResult() {
return myInvokeResult;
}
@Nullable
private static PsiElement calculateAnchorToInsertBefore(GrMemberOwner owner, PsiElement startElement) {
while (startElement != null && !isEnclosingDefinition(owner, startElement)) {
if (startElement.getParent() instanceof GroovyFile) {
return startElement.getNextSibling();
}
startElement = startElement.getParent();
PsiElement parent = startElement.getParent();
if (parent instanceof GroovyFile && ((GroovyFile) parent).getScriptClass() == owner) {
return startElement.getNextSibling();
}
}
return startElement == null ? null : startElement.getNextSibling();
}
private static boolean isEnclosingDefinition(GrMemberOwner owner, PsiElement startElement) {
if (owner instanceof GrTypeDefinition) {
GrTypeDefinition definition = (GrTypeDefinition) owner;
return startElement.getParent() == definition.getBody();
}
return false;
}
private static void renameParameterOccurrences(GrMethod method, ExtractMethodInfoHelper helper) throws IncorrectOperationException {
GrOpenBlock block = method.getBlock();
if (block == null) return;
GrStatement[] statements = block.getStatements();
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject());
for (ParameterInfo info : helper.getParameterInfos()) {
final String oldName = info.getOldName();
final String newName = info.getName();
final ArrayList<GrExpression> result = new ArrayList<GrExpression>();
if (!oldName.equals(newName)) {
for (final GrStatement statement : statements) {
statement.accept(new PsiRecursiveElementVisitor() {
public void visitElement(final PsiElement element) {
super.visitElement(element);
if (element instanceof GrReferenceExpression) {
GrReferenceExpression expr = (GrReferenceExpression) element;
if (!expr.isQualified() && oldName.equals(expr.getName())) {
result.add(expr);
}
}
}
});
for (GrExpression expr : result) {
expr.replaceWithExpression(factory.createExpressionFromText(newName), false);
}
}
}
}
}
}
@@ -1,9 +1,12 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -34,7 +37,7 @@ public class GrIntroduceContext {
public GrIntroduceContext(Project project,
Editor editor,
GrExpression expression,
@Nullable GrExpression expression,
PsiElement[] occurrences,
PsiElement scope,
@Nullable GrVariable var) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -21,7 +21,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSuperR
/**
* @author Medvedev Max
*/
class AnySupers extends GroovyRecursiveElementVisitor {
public class AnySupers extends GroovyRecursiveElementVisitor {
boolean myContainsSupers = false;
@Override
@@ -30,7 +30,7 @@ class AnySupers extends GroovyRecursiveElementVisitor {
myContainsSupers = true;
}
boolean isResult() {
public boolean isResult() {
return myContainsSupers;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -29,8 +29,6 @@ import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduceParameter.*;
import com.intellij.refactoring.ui.UsageViewDescriptorAdapter;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
import com.intellij.usageView.UsageInfo;
@@ -38,23 +36,14 @@ import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
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.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Maxim.Medvedev
@@ -99,7 +88,10 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
if (!mySettings.generateDelegate()) {
detectAccessibilityConflicts(usagesIn, conflicts);
GroovyIntroduceParameterUtil.detectAccessibilityConflicts(myContext.expression, usagesIn, conflicts,
mySettings.replaceFieldsWithGetters() != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE,
myProject
);
}
final GrMethod toReplaceIn = (GrMethod)myContext.toReplaceIn;
@@ -127,41 +119,6 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
return showConflicts(conflicts, usagesIn);
}
private void detectAccessibilityConflicts(final UsageInfo[] usageArray, MultiMap<PsiElement, String> conflicts) {
if (myContext.expression == null) return;
final ReferencedElementsCollector collector = new ReferencedElementsCollector();
myContext.expression.accept(collector);
final List<PsiElement> result = collector.getResult();
if (result.isEmpty()) return;
for (final UsageInfo usageInfo : usageArray) {
if (!(usageInfo instanceof ExternalUsageInfo) || !IntroduceParameterUtil.isMethodUsage(usageInfo)) continue;
final PsiElement place = usageInfo.getElement();
for (PsiElement element : result) {
if (element instanceof PsiField &&
mySettings.replaceFieldsWithGetters() != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE) {
//check getter access instead
final PsiClass psiClass = ((PsiField)element).getContainingClass();
LOG.assertTrue(psiClass != null);
final PsiMethod method = GroovyPropertyUtils.findGetterForField((PsiField)element);
if (method != null) {
element = method;
}
}
if (element instanceof PsiMember &&
!JavaPsiFacade.getInstance(myProject).getResolveHelper().isAccessible((PsiMember)element, place, null)) {
String message = RefactoringBundle.message(
"0.is.not.accessible.from.1.value.for.introduced.parameter.in.that.method.call.will.be.incorrect",
RefactoringUIUtil.getDescription(element, true),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(place), true));
conflicts.putValue(element, message);
}
}
}
}
@NotNull
@Override
protected UsageInfo[] findUsages() {
@@ -220,7 +177,7 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
PsiType initializerType = mySettings.getSelectedType();
// Changing external occurences (the tricky part)
// Changing external occurrences (the tricky part)
IntroduceParameterUtil.processUsages(usages, this);
@@ -229,9 +186,9 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
final boolean methodsToProcessAreDifferent = toReplaceIn != toSearchFor;
if (mySettings.generateDelegate()) {
generateDelegate(toReplaceIn);
GroovyIntroduceParameterUtil.generateDelegate(toReplaceIn, myParameterInitializer, myProject);
if (methodsToProcessAreDifferent) {
final GrMethod method = generateDelegate(toSearchFor);
final GrMethod method = GroovyIntroduceParameterUtil.generateDelegate(toSearchFor, myParameterInitializer, myProject);
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
final GrOpenBlock block = method.getBlock();
@@ -255,12 +212,12 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
if (myContext.var != null) myContext.var.delete();
// Replacing expression occurences
// Replacing expression occurrences
for (UsageInfo usage : usages) {
if (usage instanceof ChangedMethodCallInfo) {
PsiElement element = usage.getElement();
processChangedMethodCall(element);
GroovyIntroduceParameterUtil.processChangedMethodCall(element, mySettings, myProject);
}
else if (usage instanceof InternalUsageInfo) {
PsiElement element = usage.getElement();
@@ -281,86 +238,6 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl
fieldConflictsResolver.fix();
}
private GrMethod generateDelegate(PsiMethod prototype) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject);
GrMethod result;
if (prototype instanceof GrMethod) {
result = (GrMethod)prototype.copy();
}
else {
StringBuilder builder = new StringBuilder();
builder.append(prototype.getModifierList().getText()).append(' ');
if (prototype.getReturnTypeElement() != null ) {
builder.append(prototype.getReturnTypeElement().getText());
}
builder.append(' ').append(prototype.getName());
builder.append(prototype.getParameterList().getText());
builder.append("{}");
result = factory.createMethodFromText(builder.toString());
}
StringBuilder call = new StringBuilder();
call.append("def foo(){\n").append(prototype.getName()).append('(');
final GrParameter[] parameters = result.getParameters();
for (GrParameter parameter : parameters) {
call.append(parameter.getName()).append(", ");
}
call.append(myParameterInitializer.getText());
call.append(");\n}");
final GrOpenBlock block = factory.createMethodFromText(call.toString()).getBlock();
result.getBlock().replace(block);
final PsiElement parent = prototype.getParent();
return (GrMethod)parent.addBefore(result, prototype);
}
private void processChangedMethodCall(PsiElement element) {
if (element.getParent() instanceof GrMethodCallExpression) {
GrMethodCallExpression methodCall = (GrMethodCallExpression)element.getParent();
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject);
GrExpression expression = factory.createExpressionFromText(mySettings.getName(), null);
final GrArgumentList argList = methodCall.getArgumentList();
final PsiElement[] exprs = argList.getAllArguments();
if (exprs.length > 0) {
argList.addAfter(expression, exprs[exprs.length - 1]);
}
else {
argList.add(expression);
}
removeParametersFromCall(methodCall, argList);
}
else {
LOG.error(element.getParent());
}
}
private void removeParametersFromCall(GrMethodCallExpression methodCall, GrArgumentList argList) {
final GroovyResolveResult resolveResult = methodCall.advancedResolve();
final PsiElement resolved = resolveResult.getElement();
LOG.assertTrue(resolved instanceof PsiMethod);
final GrClosureSignature signature = GrClosureSignatureUtil.createSignature((PsiMethod)resolved, resolveResult.getSubstitutor());
final GrClosureSignatureUtil.ArgInfo<PsiElement>[] argInfos =
GrClosureSignatureUtil.mapParametersToArguments(signature, argList, methodCall, methodCall.getClosureArguments());
LOG.assertTrue(argInfos != null);
mySettings.parametersToRemove().forEach(new TIntProcedure() {
@Override
public boolean execute(int value) {
final List<PsiElement> args = argInfos[value].args;
for (PsiElement arg : args) {
arg.delete();
}
return true;
}
});
}
@Override
protected String getCommandName() {
return RefactoringBundle.message("introduce.parameter.command", UsageViewUtil.getDescriptiveName(myContext.toReplaceIn));
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -16,26 +16,44 @@
package org.jetbrains.plugins.groovy.refactoring.introduce.parameter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduceParameter.ExternalUsageInfo;
import com.intellij.refactoring.introduceParameter.IntroduceParameterData;
import com.intellij.refactoring.introduceParameter.IntroduceParameterUtil;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
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.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrThisReferenceExpression;
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.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
@@ -156,6 +174,143 @@ public class GroovyIntroduceParameterUtil {
});
}
public static void detectAccessibilityConflicts(@Nullable GroovyPsiElement elementToProcess,
final UsageInfo[] usages,
MultiMap<PsiElement, String> conflicts,
boolean replaceFieldsWithGetters,
Project project) {
if (elementToProcess == null) return;
final ReferencedElementsCollector collector = new ReferencedElementsCollector();
elementToProcess.accept(collector);
final List<PsiElement> result = collector.getResult();
if (result.isEmpty()) return;
for (final UsageInfo usageInfo : usages) {
if (!(usageInfo instanceof ExternalUsageInfo) || !IntroduceParameterUtil.isMethodUsage(usageInfo)) continue;
final PsiElement place = usageInfo.getElement();
for (PsiElement element : result) {
if (element instanceof PsiField && replaceFieldsWithGetters) {
//check getter access instead
final PsiClass psiClass = ((PsiField)element).getContainingClass();
LOG.assertTrue(psiClass != null);
final PsiMethod method = GroovyPropertyUtils.findGetterForField((PsiField)element);
if (method != null) {
element = method;
}
}
if (element instanceof PsiMember &&
!JavaPsiFacade.getInstance(project).getResolveHelper().isAccessible((PsiMember)element, place, null)) {
String message = RefactoringBundle.message(
"0.is.not.accessible.from.1.value.for.introduced.parameter.in.that.method.call.will.be.incorrect",
RefactoringUIUtil.getDescription(element, true),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(place), true));
conflicts.putValue(element, message);
}
}
}
}
public static void processChangedMethodCall(PsiElement element, GrIntroduceParameterSettings settings, Project project) {
if (!(element.getParent() instanceof GrMethodCallExpression)) {
LOG.error(element.getParent());
return;
}
GrMethodCallExpression methodCall = (GrMethodCallExpression)element.getParent();
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
final String name = settings.getName();
LOG.assertTrue(name != null);
GrExpression expression = factory.createExpressionFromText(name, null);
final GrArgumentList argList = methodCall.getArgumentList();
final PsiElement[] exprs = argList.getAllArguments();
if (exprs.length > 0) {
argList.addAfter(expression, exprs[exprs.length - 1]);
}
else {
argList.add(expression);
}
removeParametersFromCall(methodCall, argList, settings);
}
private static void removeParametersFromCall(GrMethodCallExpression methodCall,
GrArgumentList argList,
GrIntroduceParameterSettings settings) {
final GroovyResolveResult resolveResult = methodCall.advancedResolve();
final PsiElement resolved = resolveResult.getElement();
LOG.assertTrue(resolved instanceof PsiMethod);
final GrClosureSignature signature = GrClosureSignatureUtil.createSignature((PsiMethod)resolved, resolveResult.getSubstitutor());
final GrClosureSignatureUtil.ArgInfo<PsiElement>[] argInfos =
GrClosureSignatureUtil.mapParametersToArguments(signature, argList, methodCall, methodCall.getClosureArguments());
LOG.assertTrue(argInfos != null);
settings.parametersToRemove().forEach(new TIntProcedure() {
@Override
public boolean execute(int value) {
final List<PsiElement> args = argInfos[value].args;
for (PsiElement arg : args) {
arg.delete();
}
return true;
}
});
}
public static GrMethod generateDelegate(PsiMethod prototype, IntroduceParameterData.ExpressionWrapper initializer, Project project) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
GrMethod result;
if (prototype instanceof GrMethod) {
result = (GrMethod)prototype.copy();
}
else {
StringBuilder builder = new StringBuilder();
builder.append(prototype.getModifierList().getText()).append(' ');
if (prototype.getReturnTypeElement() != null ) {
builder.append(prototype.getReturnTypeElement().getText());
}
builder.append(' ').append(prototype.getName());
builder.append(prototype.getParameterList().getText());
builder.append("{}");
result = factory.createMethodFromText(builder.toString());
}
StringBuilder call = new StringBuilder();
call.append("def foo(){\n");
final GrParameter[] parameters = result.getParameters();
call.append(prototype.getName());
if (initializer.getExpression() instanceof GrClosableBlock) {
if (parameters.length > 0) {
call.append('(');
for (GrParameter parameter : parameters) {
call.append(parameter.getName()).append(", ");
}
call.replace(call.length()-2, call.length(), ")");
}
call.append(initializer.getText());
}
else {
call.append('(');
for (GrParameter parameter : parameters) {
call.append(parameter.getName()).append(", ");
}
call.append(initializer.getText());
call.append(")");
}
call.append("\n}");
final GrOpenBlock block = factory.createMethodFromText(call.toString()).getBlock();
result.getBlock().replace(block);
final PsiElement parent = prototype.getParent();
final GrMethod method = (GrMethod)parent.addBefore(result, prototype);
GrReferenceAdjuster.shortenReferences(method);
return method;
}
private static class FieldSearcher extends GroovyRecursiveElementVisitor {
PsiClass myClass;
private final List<PsiField> result = new ArrayList<PsiField>();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -52,8 +52,7 @@ import static com.intellij.codeInsight.ChangeContextUtil.*;
*/
public class OldReferencesResolver {
private static final Logger LOG =
Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy.OldReferencesResolver");
private static final Logger LOG = Logger.getInstance(OldReferencesResolver.class);
private final GrCall myContext;
private final GrExpression myExpr;
@@ -0,0 +1,156 @@
/*
* Copyright 2000-2012 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.extract.closure;
import com.intellij.psi.PsiElement
import gnu.trove.TIntArrayList
import org.jetbrains.annotations.NotNull
import org.jetbrains.plugins.groovy.LightGroovyTestCase
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner
import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo
import org.jetbrains.plugins.groovy.util.TestUtils
/**
* @author Max Medvedev
*/
public class ExtractClosureTest extends LightGroovyTestCase {
@Override
protected String getBasePath() {
return "${TestUtils.testDataPath}groovy/refactoring/extractMethod/";
}
private void doTest(String before, String after) {
doTest(before, after, [], [])
}
private void doTest(String before, String after, List<Integer> toRemove, List<Integer> notToUseAsParams) {
myFixture.configureByText 'a.groovy', before
def model = myFixture.editor.selectionModel
def handler = new ExtractClosureHandler() {
@Override
protected ExtractClosureHelper getSettings(@NotNull InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) {
def settings = new ExtractClosureHelper(initialInfo, owner, toSearchFor, "closure", true)
settings.setDeclareFinal(false)
settings.setGenerateDelegate(false)
settings.setName("closure")
settings.setToRemove(new TIntArrayList(toRemove as int[]))
def infos = settings.parameterInfos
for (int i: notToUseAsParams) {
infos[i].setPassAsParameter(false)
}
return settings
}
}
handler.invoke myFixture.project, myFixture.editor, myFixture.file, model.selectionStart, model.selectionEnd
myFixture.checkResult after
}
void testSimple() {
doTest('''
def foo(String s) {
s+=2
<selection>print s</selection>
}
foo('a')
''', '''
def foo(String s, Closure closure) {
s+=2
<selection>closure(s)</selection>
}
foo('a') {String s ->
print s
}
''')
}
void testRemoveUnused() {
doTest('''
class X {
def foo(String s) {
<selection>print s</selection>
}
}
new X().foo('a')
''', '''
class X {
def foo(Closure closure) {
<selection>closure()</selection>
}
}
new X().foo {->
print 'a'
}
''', [0], [0])
}
void testRemoveUnusedAndGenerateLocal() {
doTest('''
def foo(String s) {
<selection>s+=2
print s</selection>
}
foo('a')
''', '''
def foo(Closure closure) {
closure()
}
foo {->
String s = 'a'
s += 2
print s
}
''', [0], [0])
}
void testInsertQualifier() {
doTest('''
class X {
def foo(String s) {
<selection>bar()</selection>
}
def bar(){}
}
new X().foo('a')
''', '''
class X {
def foo(Closure closure) {
<selection>closure()</selection>
}
def bar(){}
}
final X x = new X()
x.foo {->
x.bar()
}
''', [0], [])
}
}
@@ -106,5 +106,6 @@ public class ExtractMethodTest extends LightGroovyTestCase {
public void testAOOBE() {doTest();}
public void testWildCardReturnType() {doTest();}
public void testParamChangedInsideExtractedMethod() {doTest();}
}
@@ -333,4 +333,5 @@ public class GrIntroduceParameterTest extends LightCodeInsightFixtureTestCase {
public void testClosureArg() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, false, false);}
public void testClosureArgWithEmptyArgList() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, true, false);}
public void testScriptMethod() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, true, false);}
}
@@ -15,7 +15,7 @@ def i = 6
i = testMethod()
private int testMethod() {
int i
def i
while (true) {
println "preved"
i = 42
@@ -11,7 +11,7 @@ protected static def getGeneratedFileNames(String name, int boo) {
}
private static ArrayList<String> testMethod() {
ArrayList<String> names
def names
names = new ArrayList<String>()
return names
}
@@ -0,0 +1,14 @@
def foo(String s){
<begin>s = 'a'
print s<end>
}
-----
def foo(String s){
testMethod()
}
private testMethod() {
String s
s = 'a'
print s
}
@@ -1,5 +1,20 @@
/*
* Copyright 2000-2012 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.
*/
def foo() {
foo("foo");
foo("foo")
}
def foo(String anObject) {
@@ -1,5 +1,20 @@
/*
* Copyright 2000-2012 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.
*/
def foo(def p) {
foo(p, "foo");
foo(p, "foo")
}
def foo(def p, String anObject) {
@@ -1,6 +1,21 @@
/*
* Copyright 2000-2012 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.
*/
class Base {
def foo() {
foo(123);
foo(123)
}
def foo(int anObject){}
@@ -8,7 +23,7 @@ class Base {
class Inh extends Base {
def foo() {
foo(123);
foo(123)
}
def foo(int anObject) {print anObject}
@@ -0,0 +1,20 @@
/*
* Copyright 2000-2012 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.
*/
def foo(String s) {
print(<selection>s + 1</selection>)
}
foo('a')
@@ -0,0 +1,20 @@
/*
* Copyright 2000-2012 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.
*/
def foo(def anObject) {
<selection>print(anObject)</selection>
}
foo('a' + 1)