convert closures

This commit is contained in:
Maxim Medvedev
2011-04-29 17:57:17 +04:00
parent 8a4c2dab1e
commit 81b2db79e0
15 changed files with 172 additions and 56 deletions
@@ -84,6 +84,7 @@ public abstract class GroovyPsiElementFactory {
public abstract GrNamedArgument createNamedArgument(String name, GrExpression expression);
public abstract GrStatement createStatementFromText(String text);
public abstract GrStatement createStatementFromText(String text, PsiElement context);
public abstract GrBlockStatement createBlockStatement(GrStatement... statements);
@@ -194,6 +195,7 @@ public abstract class GroovyPsiElementFactory {
public abstract GrDocTag createDocTagFromText(String text) ;
public abstract GrConstructorInvocation createConstructorInvocation(String text);
public abstract GrConstructorInvocation createConstructorInvocation(String text, PsiElement context);
public abstract PsiReferenceList createThrownList(PsiClassType[] exceptionTypes);
@@ -506,7 +506,12 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
}
public GrStatement createStatementFromText(String text) {
PsiFile file = createGroovyFile(text);
return createStatementFromText(text, null);
}
@Override
public GrStatement createStatementFromText(String text, PsiElement context) {
PsiFile file = createGroovyFile(text, false, context);
assert ((GroovyFileBase) file).getTopStatements()[0] instanceof GrStatement;
return (GrStatement) ((GroovyFileBase) file).getTopStatements()[0];
}
@@ -633,7 +638,12 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
@Override
public GrConstructorInvocation createConstructorInvocation(String text) {
GroovyFile file = (GroovyFile)createGroovyFile("class Foo{ def Foo(){" + text + "}}");
return createConstructorInvocation(text, null);
}
@Override
public GrConstructorInvocation createConstructorInvocation(String text, @Nullable PsiElement context) {
GroovyFile file = createGroovyFile("class Foo{ def Foo(){" + text + "}}", false, context);
return ((GrConstructor)file.getClasses()[0].getConstructors()[0]).getChainingConstructorInvocation();
}
@@ -333,8 +333,8 @@ public class TypesUtil {
return type;
}
public static PsiType boxPrimitiveType(PsiType result, PsiManager manager, GlobalSearchScope resolveScope) {
if (result instanceof PsiPrimitiveType && result != PsiType.VOID) {
public static PsiType boxPrimitiveType(PsiType result, PsiManager manager, GlobalSearchScope resolveScope, boolean boxVoid) {
if (result instanceof PsiPrimitiveType && (boxVoid || result != PsiType.VOID)) {
PsiPrimitiveType primitive = (PsiPrimitiveType)result;
String boxedTypeName = primitive.getBoxedTypeName();
if (boxedTypeName != null) {
@@ -345,6 +345,9 @@ public class TypesUtil {
return result;
}
public static PsiType boxPrimitiveType(PsiType result, PsiManager manager, GlobalSearchScope resolveScope) {
return boxPrimitiveType(result, manager, resolveScope, false);
}
@NotNull
public static PsiClassType createType(String fqName, @NotNull PsiElement context) {
return createTypeByFQClassName(fqName, context);
@@ -57,7 +57,7 @@ public class AnonymousFromMapGenerator {
substitutor = PsiSubstitutor.EMPTY;
}
builder.append("new ");
writeType(builder, type);
writeType(builder, type, operand);
builder.append("() {\n");
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(operand.getProject());
@@ -112,7 +112,7 @@ public class AnonymousFromMapGenerator {
returnType = signature.getReturnType();
}
writeType(builder, returnType);
writeType(builder, returnType, operand);
builder.append(' ').append(name);
GenerationUtil.writeParameterList(builder, parameters, new GeneratorClassNameProvider());
@@ -75,7 +75,7 @@ class ArgumentListGenerator {
for (int i = 0; i < argInfos.length; i++) {
GrClosureSignatureUtil.ArgInfo<PsiElement> arg = argInfos[i];
final GrClosureParameter param = params[i];
if (arg.isMultiArg ? generateMultiArg(arg, param, substitutor, project) : generateSingeArg(arg, param)) {
if (arg.isMultiArg ? generateMultiArg(arg, param, substitutor, project, context) : generateSingeArg(arg, param)) {
hasArgs = true;
myBuilder.append(", ");
}
@@ -111,13 +111,14 @@ class ArgumentListGenerator {
private boolean generateMultiArg(GrClosureSignatureUtil.ArgInfo<PsiElement> arg,
GrClosureParameter param,
PsiSubstitutor substitutor,
Project project) {
Project project,
GroovyPsiElement context) {
final PsiType type = param.getType();
//todo find out if param is array in case of it has declared type
if (type instanceof PsiArrayType) {
myBuilder.append("new ");
GenerationUtil.writeType(myBuilder, ((PsiArrayType)type).getComponentType());
GenerationUtil.writeType(myBuilder, ((PsiArrayType)type).getComponentType(),context);
myBuilder.append("[]{");
for (PsiElement element : arg.args) {
@@ -23,9 +23,11 @@ import com.intellij.util.ArrayUtil;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
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.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
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.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrEnumConstantInitializer;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
@@ -37,7 +39,10 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.plugins.groovy.refactoring.convertToJava.GenerationUtil.*;
@@ -51,7 +56,7 @@ public class ClassItemGeneratorImpl implements ClassItemGenerator {
public ClassItemGeneratorImpl(Project project) {
myProject = project;
classNameProvider=new GeneratorClassNameProvider();
classNameProvider = new GeneratorClassNameProvider();
}
@Override
@@ -133,12 +138,15 @@ public class ClassItemGeneratorImpl implements ClassItemGenerator {
}
builder.append(name);
final ArrayList<GrParameter> actualParams;
if (method instanceof GrMethod) {
final ArrayList<GrParameter> actualParams = getActualParams((GrMethod)method, skipOptional);
actualParams = getActualParams((GrMethod)method, skipOptional);
GenerationUtil.writeParameterList(builder, actualParams.toArray(new GrParameter[actualParams.size()]), classNameProvider);
} else {
LOG.assertTrue(skipOptional==0);
}
else {
LOG.assertTrue(skipOptional == 0);
GenerationUtil.writeParameterList(builder, method.getParameterList().getParameters(), classNameProvider);
actualParams = null;
}
@@ -147,7 +155,12 @@ public class ClassItemGeneratorImpl implements ClassItemGenerator {
if (!isAbstract) {
/************* body **********/
if (method instanceof GrMethod) {
((GrMethod)method).accept(new CodeBlockGenerator(builder, myProject));
if (skipOptional == 0) {
((GrMethod)method).accept(new CodeBlockGenerator(builder, myProject));
}
else {
builder.append("{\n").append(generateDelegateCall((GrMethod)method, actualParams)).append("\n}\n");
}
}
else if (method instanceof GrAccessorMethod) {
writeAccessorBody(builder, method);
@@ -169,6 +182,46 @@ public class ClassItemGeneratorImpl implements ClassItemGenerator {
}
}
private StringBuilder generateDelegateCall(GrMethod method, ArrayList<GrParameter> actualParams) {
final GrParameter[] parameters = method.getParameterList().getParameters();
StringBuilder builder = new StringBuilder();
if (method.isConstructor()) {
builder.append("this");
}
else {
if (method.getReturnType() != PsiType.VOID) {
builder.append("return ");
}
builder.append(method.getName());
}
builder.append("(");
for (GrParameter parameter : parameters) {
if (actualParams.contains(parameter)) {
builder.append(parameter.getName());
}
else {
LOG.assertTrue(parameter.isOptional());
final GrExpression initializer = parameter.getDefaultInitializer();
LOG.assertTrue(initializer != null);
builder.append(initializer.getText());
}
builder.append(", ");
}
builder.delete(builder.length() - 2, builder.length());
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject);
final GrStatement delegateCall;
if (method.isConstructor()) {
delegateCall = factory.createConstructorInvocation(builder.toString(), method);
}
else {
delegateCall = factory.createStatementFromText(builder.toString(), method);
}
final StringBuilder result = new StringBuilder();
delegateCall.accept(new CodeBlockGenerator(result, myProject));
return result;
}
private void writeMainScriptMethodBody(StringBuilder builder, PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass instanceof GroovyScriptClass);
@@ -222,8 +275,10 @@ public class ClassItemGeneratorImpl implements ClassItemGenerator {
if (typeDefinition instanceof GroovyScriptClass) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject);
final String name = typeDefinition.getName();
result.add(factory.createConstructorFromText(name, new String[]{"groovy.lang.Binding"}, new String[]{"binding"},"{super(binding);}", typeDefinition));
result.add(factory.createConstructorFromText(name, ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_STRING_ARRAY, "{super();}", typeDefinition));
result.add(factory.createConstructorFromText(name, new String[]{"groovy.lang.Binding"}, new String[]{"binding"}, "{super(binding);}",
typeDefinition));
result.add(
factory.createConstructorFromText(name, ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_STRING_ARRAY, "{super();}", typeDefinition));
}
return result;
}
@@ -16,19 +16,20 @@
package org.jetbrains.plugins.groovy.refactoring.convertToJava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
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.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT;
import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_LANG_CLOSURE;
import static org.jetbrains.plugins.groovy.refactoring.convertToJava.GenerationUtil.writeType;
@@ -48,23 +49,44 @@ public class ClosureGenerator {
public void generate(GrClosableBlock closure) {
final String owner = getOwner(closure);
final PsiType returnType = closure.getReturnType();
builder.append("new ").append(GROOVY_LANG_CLOSURE);
if (returnType != null) {
builder.append("<");
writeType(builder, returnType);
builder.append(">(");
}
builder.append("new ");
writeType(builder, closure.getType(), closure);
builder.append('(');
builder.append(owner).append(", ").append(owner).append(") {\n");
builder.append("public ").append(JAVA_LANG_OBJECT).append(" doCall(");
final GrParameter[] parameters = closure.getAllParameters();
for (GrParameter parameter : parameters) {
final String name = parameter.getName();
final GrMethod method = generateClosureMethod(closure);
ClassGenerator.writeAllSignaturesOfMethod(builder, method, new ClassItemGeneratorImpl(context.project));
builder.append("}");
}
private GrMethod generateClosureMethod(GrClosableBlock block) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(context.project);
final GrMethod method = factory.createMethodFromText("def doCall(){}");
final PsiElement first;
if (block.hasParametersSection()) {
method.getParameterList().replace(block.getParameterList());
final PsiElement arrow = block.getArrow();
LOG.assertTrue(arrow != null);
first = arrow.getNextSibling();
}
else {
final GrParameter[] allParameters = block.getAllParameters();
LOG.assertTrue(allParameters.length == 1);
final GrParameter itParameter = allParameters[0];
final GrParameter parameter = factory.createParameter("it", itParameter.getType().getCanonicalText(), "null", block);
method.getParameterList().addParameterToEnd(parameter);
final PsiElement lBrace = block.getLBrace();
LOG.assertTrue(lBrace != null);
first = lBrace.getNextSibling();
}
final GrStatement[] statements = closure.getStatements();
final PsiElement rBrace = block.getRBrace();
final PsiElement last = rBrace != null ? rBrace.getPrevSibling() : block.getLastChild();
final GrOpenBlock methodBlock = method.getBlock();
methodBlock.addRange(first, last);
return method;
}
private static String getOwner(GrClosableBlock closure) {
@@ -75,7 +97,7 @@ public class ClosureGenerator {
LOG.assertTrue(false, "closure must have member parent");
return "this";
}
if (context instanceof GrMember && ((GrMember)context).hasModifierProperty(GrModifier.STATIC)) {
if (context instanceof GrMember && ((GrMember)context).hasModifierProperty(PsiModifier.STATIC)) {
return "null";
}
return "this";
@@ -329,7 +329,7 @@ public class CodeBlockGenerator extends Generator {
if (GenerationUtil.writeModifiers(builder, var.getModifierList())) {
builder.append(" ");
}
GenerationUtil.writeType(builder, GenerationUtil.getVarType(var));
GenerationUtil.writeType(builder, GenerationUtil.getVarType(var), var);
builder.append(" ").append(var.getName());
}
@@ -451,7 +451,7 @@ public class CodeBlockGenerator extends Generator {
for (final GrVariable v : variables) {
GenerationUtil.writeModifiers(builder, modifierList);
final PsiType type = GenerationUtil.getVarType(v);
GenerationUtil.writeType(builder, type);
GenerationUtil.writeType(builder, type, variableDeclaration);
builder.append(" ").append(v.getName());
builder.append(" = ").append(iteratorName).append(".hasNext() ? ").append(iteratorName).append(".next() : null;");
}
@@ -142,7 +142,7 @@ public class ExpressionGenerator extends Generator {
if (hasFieldInitialization) {
builder = new StringBuilder();
varName = suggestVarName(type, newExpression, this.context);
writeType(builder, type);
writeType(builder, type, newExpression);
builder.append(" ").append(varName).append(" = ");
}
else {
@@ -165,7 +165,7 @@ public class ExpressionGenerator extends Generator {
final PsiType builtIn = typeElement.getType();
LOG.assertTrue(builtIn instanceof PsiPrimitiveType);
final PsiType boxed = TypesUtil.boxPrimitiveType(builtIn, newExpression.getManager(), newExpression.getResolveScope());
writeType(builder, boxed);
writeType(builder, boxed, newExpression);
}
else if (referenceElement != null) {
writeCodeReferenceElement(builder, referenceElement);
@@ -653,7 +653,7 @@ public class ExpressionGenerator extends Generator {
final String name = suggestVarName(initializer, context);
final StringBuilder builder = new StringBuilder();
builder.append("final ");
writeType(builder, initializer.getType());
writeType(builder, initializer.getType(), initializer);
builder.append(' ').append(name).append(" = ");
initializer.accept(new ExpressionGenerator(builder, context));
builder.append(';');
@@ -824,7 +824,7 @@ public class ExpressionGenerator extends Generator {
else {
boolean isActuallyList = type instanceof GrTupleType;
builder.append("new ");
writeType(builder, type);
writeType(builder, type, listOrMap);
if (isActuallyList) {
builder.append("(java.util.Arrays.asList(");
}
@@ -876,10 +876,10 @@ public class ExpressionGenerator extends Generator {
private String generateMapVariableDeclaration(GrListOrMap listOrMap, PsiType type) {
StringBuilder declaration = new StringBuilder();
writeType(declaration, type);
writeType(declaration, type, listOrMap);
final String varName = suggestVarName(type, listOrMap, this.context);
declaration.append(" ").append(varName).append(" = new ");
writeType(declaration, type);
writeType(declaration, type, listOrMap);
declaration.append("(");
//insert count of elements in list or map
@@ -80,8 +80,8 @@ public class GenerationUtil {
private GenerationUtil() {
}
public static void writeType(StringBuilder builder, PsiType type) {
builder.append(type.getCanonicalText()); //todo make smarter.
public static void writeType(StringBuilder builder, PsiType type, PsiElement context) {
writeType(builder, type, context, new GeneratorClassNameProvider());
}
public static void writeType(final StringBuilder builder,
@@ -143,7 +143,7 @@ public class GenerationUtil {
final String qname = classNameProvider.getQualifiedClassName(psiClass, context);
builder.append(qname);
}
writeTypeParameters(builder, parameters);
writeTypeParameters(builder, parameters, context, classNameProvider);
return this;
}
@@ -189,12 +189,18 @@ public class GenerationUtil {
((PsiParameterList)parent).getParameterIndex((PsiParameter)context) == ((PsiParameterList)parent).getParametersCount() - 1;
}
private static void writeTypeParameters(StringBuilder builder, PsiType[] parameters) {
private static void writeTypeParameters(StringBuilder builder,
PsiType[] parameters,
PsiElement context,
ClassNameProvider classNameProvider) {
if (parameters.length == 0) return;
builder.append("<");
for (PsiType parameter : parameters) {
writeType(builder, parameter);
if (parameter instanceof PsiPrimitiveType) {
parameter = TypesUtil.boxPrimitiveType(parameter, context.getManager(), context.getResolveScope(), true);
}
writeType(builder, parameter, context, classNameProvider);
builder.append(", ");
}
builder.replace(builder.length() - 2, builder.length(), ">");
@@ -238,7 +244,7 @@ public class GenerationUtil {
else {
builder.append(((PsiPackage)resolved).getQualifiedName());
}
writeTypeParameters(builder, referenceElement.getTypeArguments());
writeTypeParameters(builder, referenceElement.getTypeArguments(), referenceElement, new GeneratorClassNameProvider());
}
public static void invokeMethodByName(GrExpression caller,
@@ -460,7 +466,7 @@ public class GenerationUtil {
if (initializer != null) {
final PsiType varType = initializer.getType();
if (varType != null) {
types.add(getTypeText(varType));
types.add(getTypeText(varType, variableDeclaration));
}
}
}
@@ -469,9 +475,9 @@ public class GenerationUtil {
return types;
}
static String getTypeText(PsiType varType) {
static String getTypeText(PsiType varType, PsiElement context) {
final StringBuilder builder = new StringBuilder();
writeType(builder, varType);
writeType(builder, varType, context);
return builder.toString();
}
@@ -522,7 +528,7 @@ public class GenerationUtil {
return;
}
PsiType type = getVarType(variables[0]);
writeType(builder, type);
writeType(builder, type, variableDeclaration);
builder.append(" ");
for (GrVariable variable : variables) {
@@ -551,7 +557,7 @@ public class GenerationUtil {
builder.append(" ");
}
writeType(builder, type);
writeType(builder, type, variable);
builder.append(" ");
writeVariableWithoutType(builder, expressionContext, variable);
@@ -131,7 +131,7 @@ public class SwitchStatementGenerator {
final PsiType type = condition.getType();
final String varName = GenerationUtil.validateName("switchArg", condition, context);
conditionBuilder.append("final ");
GenerationUtil.writeType(conditionBuilder, type);
GenerationUtil.writeType(conditionBuilder, type, condition);
conditionBuilder.append(" ").append(varName).append(" = ");
condition.accept(new ExpressionGenerator(conditionBuilder, context));
conditionBuilder.append(";\n");
@@ -127,7 +127,7 @@ class Bar {
myFixture.addFileToProject("java/util/regex/Pattern.java", """
package java.util.regex;
final class Pattern {
public final class Pattern {
public static Pattern compile(String regex) {return new Pattern();}
public Matcher matcher(CharSequence input){return new Matcher();}
public static boolean matches(String regex, CharSequence input) {return true;}
@@ -136,7 +136,7 @@ final class Pattern {
myFixture.addFileToProject("java/util/regex/Matcher.java", """
package java.util.regex;
final class Matcher {
public final class Matcher {
public boolean matches() {return true;}
}
@@ -148,4 +148,5 @@ final class Matcher {
void testCharInitializer() {doTest()}
void testAnonymousFromMap() {doTest()}
void testClosure() {doTest()}
}
@@ -0,0 +1,4 @@
def list = [1, 2, 3]
list.each {
print it
}
@@ -0,0 +1,11 @@
java.util.ArrayList<java.lang.Integer> list = new java.util.ArrayList<java.lang.Integer>(java.util.Arrays.asList(1, 2, 3));
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(list, new groovy.lang.Closure<java.lang.Void>(this, this) {
public java.lang.Object doCall(java.lang.Object it) {
print(it);
}
public java.lang.Object doCall() {
return doCall(null);
}
});
@@ -10,5 +10,6 @@ java.lang.Integer i = 23;
E(int x) {
}
E() {
this(2);
}
}