Convert to Java: this & super refs should have qualifiers if used inside closures

This commit is contained in:
Max Medvedev
2012-12-17 15:50:49 +04:00
parent bd65dcbcd1
commit cd7dd016aa
11 changed files with 119 additions and 33 deletions
@@ -687,7 +687,7 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
text = qname + ".this";
}
}
return createReferenceExpressionFromText(text);
return createReferenceExpressionFromText(text, psiClass);
}
@Override
@@ -491,7 +491,7 @@ public class PsiUtil {
return null;
}
private static boolean isInDummyFile(@NotNull PsiElement context) {
public static boolean isInDummyFile(@NotNull PsiElement context) {
PsiFile file = context.getContainingFile();
if (file == null) return false;
@@ -16,9 +16,7 @@
package org.jetbrains.plugins.groovy.refactoring.convertToJava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -30,7 +28,6 @@ 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.GrClosableBlock;
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 org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod;
@@ -58,11 +55,16 @@ public class ClosureGenerator {
}
public void generate(@NotNull GrClosableBlock closure) {
final String owner = getOwner(closure);
builder.append("new ");
writeTypeForNew(builder, closure.getType(), closure);
builder.append('(');
builder.append(owner).append(", ").append(owner).append(") {\n");
final CharSequence owner = getOwner(closure);
builder.append(owner);
builder.append(", ");
builder.append(owner);
builder.append(") {\n");
generateClosureMainMethod(closure);
@@ -119,17 +121,39 @@ public class ClosureGenerator {
@NonNls
@NotNull
private static String getOwner(@NotNull GrClosableBlock closure) {
final GroovyPsiElement context = PsiTreeUtil.getParentOfType(closure, GrMember.class, GrClosableBlock.class, GroovyFile.class);
private CharSequence getOwner(@NotNull GrClosableBlock closure) {
final GroovyPsiElement context = PsiTreeUtil.getParentOfType(closure, GrMember.class, GroovyFile.class);
LOG.assertTrue(context != null);
if (context instanceof GrTypeDefinition) {
LOG.error("closure must have member parent");
final PsiClass contextClass;
if (context instanceof GroovyFile) {
contextClass = ((GroovyFile)context).getScriptClass();
}
else if (context instanceof PsiClass) {
contextClass = (PsiClass)context;
}
else if (context instanceof GrMember) {
if (((GrMember)context).hasModifierProperty(PsiModifier.STATIC)) {
contextClass = null; //no context class
}
else {
contextClass = ((GrMember)context).getContainingClass();
}
}
else {
contextClass = null;
}
if (contextClass == null) return "null";
final PsiElement implicitClass = GenerationUtil.getWrappingImplicitClass(closure);
if (implicitClass == null) {
return "this";
}
if (context instanceof GrMember && ((GrMember)context).hasModifierProperty(PsiModifier.STATIC)) {
return "null";
else {
final StringBuilder buffer = new StringBuilder();
GenerationUtil.writeThisReference(contextClass, buffer, this.context);
return buffer;
}
return "this";
}
}
@@ -915,12 +915,10 @@ public class ExpressionGenerator extends Generator {
final GrExpression qualifier = referenceExpression.getQualifier();
final GroovyResolveResult resolveResult = referenceExpression.advancedResolve();
final PsiElement resolved = resolveResult.getElement();
final String referenceName = referenceExpression.getReferenceName();
if (PsiUtil.isThisOrSuperRef(referenceExpression)) {
if (!context.isInAnonymousContext() && qualifier != null) {
qualifier.accept(this);
}
builder.append(referenceExpression.getReferenceName());
writeThisOrSuperRef(referenceExpression, qualifier, referenceName);
return;
}
@@ -939,8 +937,8 @@ public class ExpressionGenerator extends Generator {
}
//don't try to resolve local vars that are provided my this generator (they are listed in myUsedVarNames)
if (resolved == null && qualifier == null && context.myUsedVarNames.contains(referenceExpression.getReferenceName())) {
builder.append(referenceExpression.getReferenceName());
if (resolved == null && qualifier == null && context.myUsedVarNames.contains(referenceName)) {
builder.append(referenceName);
return;
}
@@ -965,7 +963,7 @@ public class ExpressionGenerator extends Generator {
LOG.assertTrue(qualifier != null);
builder.append("new ").append(GroovyCommonClassNames.ORG_CODEHAUS_GROOVY_RUNTIME_METHOD_CLOSURE).append('(');
qualifier.accept(this);
builder.append(", \"").append(referenceExpression.getReferenceName()).append("\")");
builder.append(", \"").append(referenceName).append("\")");
return;
}
@@ -1018,7 +1016,7 @@ public class ExpressionGenerator extends Generator {
}
else {
//unresolved reference
final String refName = referenceExpression.getReferenceName();
final String refName = referenceName;
if (refName != null) {
if (PsiUtil.isAccessedForWriting(referenceExpression)) {
builder.append(refName);
@@ -1057,6 +1055,35 @@ public class ExpressionGenerator extends Generator {
}
private void writeThisOrSuperRef(GrReferenceExpression referenceExpression, GrExpression qualifier, String referenceName) {
if (!context.isInAnonymousContext() && qualifier != null) {
qualifier.accept(this);
builder.append('.');
builder.append(referenceName);
}
else if (getWrappingImplicitClass(referenceExpression) != null) {
final PsiClass contextClass;
if ("this".equals(referenceName)) {
final PsiElement _contextClass = referenceExpression.resolve();
if (_contextClass instanceof PsiClass) {
contextClass = (PsiClass)_contextClass;
}
else {
contextClass = null;
}
writeThisReference(contextClass, builder, context);
}
else { //super ref
//super ref is used without qualifier. So we should use context class as a qualifier
contextClass = PsiUtil.getContextClass(referenceExpression);
writeSuperReference(contextClass, builder, context);
}
}
else {
builder.append(referenceName);
}
}
private String createVarByInitializer(@NotNull GrExpression initializer) {
GrExpression inner = initializer;
while (inner instanceof GrParenthesizedExpression) inner = ((GrParenthesizedExpression)inner).getOperand();
@@ -27,6 +27,7 @@ import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrClassSubstitutor;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
@@ -648,4 +649,38 @@ public class GenerationUtil {
static PsiType getNotNullType(PsiElement context, PsiType type) {
return type != null ? type : TypesUtil.getJavaLangObject(context);
}
public static void writeThisReference(@Nullable PsiClass targetClass, StringBuilder buffer, ExpressionContext context) {
if (targetClass != null && !(targetClass instanceof PsiAnonymousClass)) {
final GrCodeReferenceElement ref = GroovyPsiElementFactory.getInstance(context.project).createCodeReferenceElementFromClass(targetClass);
writeCodeReferenceElement(buffer, ref);
buffer.append('.');
}
buffer.append("this");
}
public static void writeSuperReference(@Nullable PsiClass targetClass, StringBuilder buffer, ExpressionContext context) {
if (targetClass != null && !(targetClass instanceof PsiAnonymousClass)) {
final GrCodeReferenceElement ref = GroovyPsiElementFactory.getInstance(context.project).createCodeReferenceElementFromClass(targetClass);
writeCodeReferenceElement(buffer, ref);
buffer.append('.');
}
buffer.append("super");
}
@Nullable
static PsiElement getWrappingImplicitClass(@NotNull PsiElement place) {
PsiElement parent = place.getParent();
while (parent != null) {
if (parent instanceof PsiClass) return null;
if (parent instanceof GroovyFile && !(PsiUtil.isInDummyFile(parent))) return null;
if (parent instanceof GrClosableBlock) return parent;
parent = parent.getContext();
}
return null;
}
}
@@ -1,6 +1,6 @@
print(new java.lang.Runnable() {
public void run(java.lang.Object it) {
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, "foo}");
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(anonymousFromMap.this, "foo}");
}
public void run() {
this.run(null);
@@ -1,7 +1,7 @@
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 void doCall(java.lang.Object it) {
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, it);
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(closure.this, it);
}
public void doCall() {
@@ -8,7 +8,7 @@ this.foo = foo;
private groovy.lang.Closure<java.lang.Integer> foo = new groovy.lang.Closure<java.lang.Integer>(this, this) {
public java.lang.Integer doCall(int x) {
final groovy.lang.Reference<java.lang.Integer> i1 = new groovy.lang.Reference<java.lang.Integer>(x);
return org.codehaus.groovy.runtime.DefaultGroovyMethods.each(1, new groovy.lang.Closure<java.lang.Void>(this, this) {
return org.codehaus.groovy.runtime.DefaultGroovyMethods.each(1, new groovy.lang.Closure<java.lang.Void>(X.this, X.this) {
public void doCall(java.lang.Object it) {
i1.set(2);
int i = 3;
@@ -12,7 +12,7 @@ public void foo(int x) {
final groovy.lang.Reference<java.lang.Integer> i = new groovy.lang.Reference<java.lang.Integer>(x);
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(new java.util.ArrayList<java.lang.Integer>(java.util.Arrays.asList(1, 2, 3)), new groovy.lang.Closure<java.lang.Integer>(this, this) {
public java.lang.Integer doCall(java.lang.Object it) {
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, i.get());
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(methodParamInClosureImplicitReturn.this, i.get());
return setGroovyRef(i, i.get() + 1);
}
@@ -23,7 +23,7 @@ return doCall(null);
});
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(new java.util.ArrayList<java.lang.Integer>(java.util.Arrays.asList(1, 2, 3)), new groovy.lang.Closure<java.lang.Integer>(this, this) {
public java.lang.Integer doCall(java.lang.Object it) {
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, i.get());
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(methodParamInClosureImplicitReturn.this, i.get());
i.set(i.get()++);
return i.get();
}
@@ -1,9 +1,9 @@
public class X {
public void foo() {
final groovy.lang.Reference<java.lang.Integer> ab = new groovy.lang.Reference<java.lang.Integer>(4);
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(this, new groovy.lang.Closure<java.lang.Object>(this, this) {
public java.lang.Object doCall(java.lang.Object it) {
return org.codehaus.groovy.runtime.DefaultGroovyMethods.each(this, new groovy.lang.Closure<java.lang.Integer>(this, this) {
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(this, new groovy.lang.Closure<X>(this, this) {
public X doCall(java.lang.Object it) {
return org.codehaus.groovy.runtime.DefaultGroovyMethods.each(X.this, new groovy.lang.Closure<java.lang.Integer>(X.this, X.this) {
public java.lang.Integer doCall(java.lang.Object it) {
return setGroovyRef(ab, 2);
}
@@ -15,7 +15,7 @@ return doCall(null);
});
}
public java.lang.Object doCall() {
public X doCall() {
return doCall(null);
}
@@ -11,7 +11,7 @@ foo.set(foo.get()++);
foo.set(foo.get() + 2);
foo.set(foo.get() - 1);
foo.set(4);
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, foo.get());
org.codehaus.groovy.runtime.DefaultGroovyMethods.print(refInClosureInScript.this, foo.get());
}
public void doCall() {