blob: aceef3c34b846fec520c608145eae1995c1a9fa3 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring.convertToJava;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.GrInspectionUtil;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.intentions.conversions.strings.ConvertGStringToStringIntention;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
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;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
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.arguments.GrArgumentLabel;
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.branch.GrReturnStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
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.expressions.path.GrPropertySelection;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SubtypeConstraint;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrLiteralClassType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrRangeType;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.ClosureSyntheticParameter;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer;
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.refactoring.convertToJava.invocators.CustomMethodInvocator;
/**
* @author Maxim.Medvedev
*/
public class ExpressionGenerator extends Generator {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.convertToJava.ExpressionGenerator");
private final StringBuilder builder;
private final GroovyPsiElementFactory factory;
private final ExpressionContext context;
public ExpressionGenerator(StringBuilder builder, ExpressionContext context) {
this.builder = builder;
this.context = context;
factory = GroovyPsiElementFactory.getInstance(context.project);
}
public ExpressionGenerator(ExpressionContext context) {
this.builder = new StringBuilder();
this.context = context;
factory = GroovyPsiElementFactory.getInstance(context.project);
}
@Override
public StringBuilder getBuilder() {
return builder;
}
@Override
public ExpressionContext getContext() {
return context;
}
@Override
public void visitClosure(GrClosableBlock closure) {
new ClosureGenerator(builder, context).generate(closure);
}
@Override
public void visitExpression(GrExpression expression) {
LOG.error("this method should not be invoked");
}
@Override
public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) {
generateMethodCall(methodCallExpression);
}
private void generateMethodCall(GrMethodCall methodCallExpression) {
final GrExpression invoked = methodCallExpression.getInvokedExpression();
final GrExpression[] exprs = methodCallExpression.getExpressionArguments();
final GrNamedArgument[] namedArgs = methodCallExpression.getNamedArguments();
final GrClosableBlock[] clArgs = methodCallExpression.getClosureArguments();
if (invoked instanceof GrReferenceExpression) {
final GroovyResolveResult resolveResult = ((GrReferenceExpression)invoked).advancedResolve();
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PsiMethod) {
final GrExpression qualifier = ((GrReferenceExpression)invoked).getQualifier();//todo replace null-qualifier with this-reference
invokeMethodOn(
((PsiMethod)resolved),
qualifier,
exprs, namedArgs, clArgs,
resolveResult.getSubstitutor(),
methodCallExpression
);
return;
}
else if (resolved == null) {
final GrExpression qualifier = ((GrReferenceExpression)invoked).getQualifier();
final GrExpression[] args =
generateArgsForInvokeMethod(((GrReferenceExpression)invoked).getReferenceName(), exprs, namedArgs, clArgs, methodCallExpression);
GenerationUtil.invokeMethodByName(qualifier, "invokeMethod", args, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, this, methodCallExpression);
return;
}
}
GenerationUtil.invokeMethodByName(invoked, "call", exprs, namedArgs, clArgs, this, methodCallExpression);
}
private GrExpression[] generateArgsForInvokeMethod(String name,
GrExpression[] exprs,
GrNamedArgument[] namedArgs,
GrClosableBlock[] clArgs,
GroovyPsiElement psiContext) {
GrExpression[] result = new GrExpression[2];
result[0] = factory.createExpressionFromText("\"" + name + "\"");
StringBuilder builder = new StringBuilder();
builder.append('[');
if (namedArgs.length > 0) {
builder.append('[');
for (GrNamedArgument namedArg : namedArgs) {
builder.append(namedArg.getText()).append(',');
}
builder.delete(builder.length() - 1, builder.length());
//builder.removeFromTheEnd(1);
builder.append("],");
}
for (GrExpression expr : exprs) {
builder.append(expr.getText()).append(',');
}
for (GrClosableBlock clArg : clArgs) {
builder.append(clArg.getText()).append(',');
}
if (namedArgs.length + exprs.length + clArgs.length > 0) builder.delete(builder.length() - 1, builder.length());
//if (namedArgs.length + exprs.length + clArgs.length > 0) builder.removeFromTheEnd(1);
builder.append("] as Object[]");
result[1] = factory.createExpressionFromText(builder.toString(), psiContext);
return result;
}
@Override
public void visitNewExpression(GrNewExpression newExpression) {
boolean hasFieldInitialization = hasFieldInitialization(newExpression);
StringBuilder builder;
final PsiType type = newExpression.getType();
final String varName;
if (hasFieldInitialization) {
builder = new StringBuilder();
varName = GenerationUtil.suggestVarName(type, newExpression, context);
TypeWriter.writeType(builder, type, newExpression);
builder.append(' ').append(varName).append(" = ");
}
else {
varName = null;
builder = this.builder;
}
final GrExpression qualifier = newExpression.getQualifier();
if (qualifier != null) {
qualifier.accept(this);
builder.append('.');
}
final GrTypeElement typeElement = newExpression.getTypeElement();
final GrArrayDeclaration arrayDeclaration = newExpression.getArrayDeclaration();
final GrCodeReferenceElement referenceElement = newExpression.getReferenceElement();
builder.append("new ");
if (typeElement != null) {
final PsiType builtIn = typeElement.getType();
LOG.assertTrue(builtIn instanceof PsiPrimitiveType);
final PsiType boxed = TypesUtil.boxPrimitiveType(builtIn, newExpression.getManager(), newExpression.getResolveScope());
TypeWriter.writeTypeForNew(builder, boxed, newExpression);
}
else if (referenceElement != null) {
GenerationUtil.writeCodeReferenceElement(builder, referenceElement);
}
final GrArgumentList argList = newExpression.getArgumentList();
if (argList != null) {
GrClosureSignature signature = null;
final GroovyResolveResult resolveResult = newExpression.advancedResolve();
final PsiElement constructor = resolveResult.getElement();
if (constructor instanceof PsiMethod) {
signature = GrClosureSignatureUtil.createSignature((PsiMethod)constructor, resolveResult.getSubstitutor());
}
else if (referenceElement != null) {
final GroovyResolveResult clazzResult = referenceElement.advancedResolve();
final PsiElement clazz = clazzResult.getElement();
if (clazz instanceof PsiClass && ((PsiClass)clazz).getConstructors().length == 0) {
signature = GrClosureSignatureUtil.createSignature(PsiParameter.EMPTY_ARRAY, null);
}
}
final GrNamedArgument[] namedArgs = hasFieldInitialization ? GrNamedArgument.EMPTY_ARRAY : argList.getNamedArguments();
new ArgumentListGenerator(builder, context).generate(
signature,
argList.getExpressionArguments(),
namedArgs,
GrClosableBlock.EMPTY_ARRAY,
newExpression
);
}
final GrAnonymousClassDefinition anonymous = newExpression.getAnonymousClassDefinition();
if (anonymous != null) {
writeTypeBody(builder, anonymous);
}
if (arrayDeclaration != null) {
final GrExpression[] boundExpressions = arrayDeclaration.getBoundExpressions();
for (GrExpression boundExpression : boundExpressions) {
builder.append('[');
boundExpression.accept(this);
builder.append(']');
}
if (boundExpressions.length == 0) {
builder.append("[]");
}
}
if (hasFieldInitialization) {
builder.append(';');
context.myStatements.add(builder.toString());
final GrNamedArgument[] namedArguments = argList.getNamedArguments();
for (GrNamedArgument namedArgument : namedArguments) {
final String fieldName = namedArgument.getLabelName();
if (fieldName == null) {
//todo try to initialize field
final GrArgumentLabel label = namedArgument.getLabel();
LOG.info("cannot initialize field " + (label == null ? "<null>" : label.getText()));
}
else {
final GroovyResolveResult resolveResult = referenceElement.advancedResolve();
final PsiElement resolved = resolveResult.getElement();
LOG.assertTrue(resolved instanceof PsiClass);
initializeField(varName, type, ((PsiClass)resolved), resolveResult.getSubstitutor(), fieldName, namedArgument.getExpression());
}
}
}
}
private void initializeField(String varName,
PsiType type,
PsiClass resolved,
PsiSubstitutor substitutor,
String fieldName,
GrExpression expression) {
StringBuilder builder = new StringBuilder();
final PsiMethod setter = GroovyPropertyUtils.findPropertySetter(resolved, fieldName, false, true);
if (setter != null) {
final GrVariableDeclaration var = factory.createVariableDeclaration(ArrayUtil.EMPTY_STRING_ARRAY, "", type, varName);
final GrReferenceExpression caller = factory.createReferenceExpressionFromText(varName, var);
invokeMethodOn(setter, caller, new GrExpression[]{expression}, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, substitutor,
expression);
}
else {
builder.append(varName).append('.').append(fieldName).append(" = ");
expression.accept(new ExpressionGenerator(builder, context));
}
context.myStatements.add(builder.toString());
}
@Override
public String toString() {
return builder.toString();
}
private static boolean hasFieldInitialization(GrNewExpression newExpression) {
final GrArgumentList argumentList = newExpression.getArgumentList();
if (argumentList == null) return false;
if (argumentList.getNamedArguments().length == 0) return false;
final GrCodeReferenceElement refElement = newExpression.getReferenceElement();
if (refElement == null) return false;
final GroovyResolveResult resolveResult = newExpression.advancedResolve();
final PsiElement constructor = resolveResult.getElement();
if (constructor instanceof PsiMethod) {
return ((PsiMethod)constructor).getParameterList().getParametersCount() == 0;
}
final PsiElement resolved = refElement.resolve();
return resolved instanceof PsiClass;
}
private void writeTypeBody(StringBuilder builder, GrAnonymousClassDefinition anonymous) {
builder.append("{\n");
final GeneratorClassNameProvider classNameProvider = new GeneratorClassNameProvider();
final ClassItemGeneratorImpl classItemGenerator = new ClassItemGeneratorImpl(context.extend());
new ClassGenerator(classNameProvider, classItemGenerator).writeMembers(builder, anonymous);
builder.append('}');
}
@Override
public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
generateMethodCall(applicationStatement);
}
@Override
public void visitConditionalExpression(GrConditionalExpression expression) {
final GrExpression condition = expression.getCondition();
final GrExpression thenBranch = expression.getThenBranch();
final GrExpression elseBranch = expression.getElseBranch();
final boolean elvis = expression instanceof GrElvisExpression;
final String var;
if (elvis) {
var = createVarByInitializer(condition);
}
else {
var = null;
}
final PsiType type = condition.getType();
if (type == null || TypesUtil.unboxPrimitiveTypeWrapper(type) == PsiType.BOOLEAN) {
if (elvis) {
builder.append(var);
}
else {
condition.accept(this);
}
}
else {
final GroovyResolveResult[] results = ResolveUtil.getMethodCandidates(type, "asBoolean", expression, PsiType.EMPTY_ARRAY);
final GroovyResolveResult result = PsiImplUtil.extractUniqueResult(results);
GenerationUtil.invokeMethodByResolveResult(
elvis ? factory.createReferenceExpressionFromText(var, expression) : condition,
result,
"asBoolean",
GrExpression.EMPTY_ARRAY, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY,
this,
expression
);
}
builder.append('?');
if (thenBranch != null) {
if (elvis) {
builder.append(var);
}
else {
thenBranch.accept(this);
}
}
builder.append(':');
if (elseBranch != null) {
elseBranch.accept(this);
}
}
/**
* x= expr ->
* x = expr
* x.set(expr)[: x.get()]
* x+= expr ->
* x+=expr
* x= plus(x, expr)
* x.set(plus(x, expr))[expr]
* x[a] = 4 ->
* x[a] = 4
* x.putAt(a, 4) [4]
*/
@Override
public void visitAssignmentExpression(final GrAssignmentExpression expression) {
final GrExpression lValue = expression.getLValue();
final GrExpression rValue = expression.getRValue();
final IElementType token = expression.getOperationTokenType();
PsiElement realLValue = PsiUtil.skipParentheses(lValue, false);
if (realLValue instanceof GrReferenceExpression && rValue != null) {
GroovyResolveResult resolveResult = ((GrReferenceExpression)realLValue).advancedResolve();
PsiElement resolved = resolveResult.getElement();
if (resolved instanceof GrVariable && context.analyzedVars.toWrap((GrVariable)resolved)) {
//write assignment to wrapped local var
writeAssignmentWithRefSetter((GrExpression)realLValue, expression);
return;
}
else if (resolved instanceof PsiMethod && resolveResult.isInvokedOnProperty()) {
//write assignment via setter
writeAssignmentWithSetter(((GrReferenceExpression)realLValue).getQualifier(), (PsiMethod)resolved, expression);
return;
}
else if (resolved == null || resolved instanceof GrBindingVariable) {
//write unresolved reference assignment via setter GroovyObject.setProperty(String name, Object value)
final GrExpression qualifier = ((GrReferenceExpression)realLValue).getQualifier();
final PsiType type = PsiImplUtil.getQualifierType((GrReferenceExpression)realLValue);
final GrExpression[] args = {
factory.createExpressionFromText("\"" + ((GrReferenceExpression)realLValue).getReferenceName() + "\""),
getRValue(expression)
};
GroovyResolveResult[] candidates = type != null
? ResolveUtil.getMethodCandidates(type, "setProperty", expression, args[0].getType(), args[1].getType())
: GroovyResolveResult.EMPTY_ARRAY;
final PsiMethod method = PsiImplUtil.extractUniqueElement(candidates);
if (method != null) {
writeAssignmentWithSetter(qualifier, method, args, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, PsiSubstitutor.EMPTY, expression);
return;
}
}
}
else if (realLValue instanceof GrIndexProperty) { //qualifier[args] = rValue
//write assignment via qualifier.putAt(Args, Value) method
final GroovyResolveResult result = PsiImplUtil.extractUniqueResult(((GrIndexProperty)realLValue).multiResolve(false));
final PsiElement resolved = result.getElement();
if (resolved instanceof PsiMethod) {
final GrExpression[] args = ((GrIndexProperty)realLValue).getArgumentList().getExpressionArguments();
writeAssignmentWithSetter(((GrIndexProperty)realLValue).getInvokedExpression(), (PsiMethod)resolved,
ArrayUtil.append(args, getRValue(expression)), GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY,
result.getSubstitutor(), expression);
return;
}
}
final PsiType lType = GenerationUtil.getDeclaredType(lValue, context);
if (token == GroovyTokenTypes.mASSIGN) {
//write simple assignment
lValue.accept(this);
builder.append(" = ");
if (rValue != null) {
final PsiType rType = GenerationUtil.getDeclaredType(rValue, context);
GenerationUtil.wrapInCastIfNeeded(builder, GenerationUtil.getNotNullType(expression, lType), rType, expression, context, new StatementWriter() {
@Override
public void writeStatement(StringBuilder builder, ExpressionContext context) {
rValue.accept(ExpressionGenerator.this);
}
});
}
}
else {
//write assignment such as +=, -=, etc
final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(expression.multiResolve(false));
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PsiMethod && !shouldNotReplaceOperatorWithMethod(lValue.getType(), rValue, expression.getOperationTokenType())) {
lValue.accept(this);
builder.append(" = ");
final PsiType rType = GenerationUtil.getDeclaredType((PsiMethod)resolved, resolveResult.getSubstitutor(), context);
GenerationUtil.wrapInCastIfNeeded(builder, GenerationUtil.getNotNullType(expression, lType), rType, expression, context, new StatementWriter() {
@Override
public void writeStatement(StringBuilder builder, ExpressionContext context) {
invokeMethodOn(
((PsiMethod)resolved),
(GrExpression)lValue.copy(),
rValue == null ? GrExpression.EMPTY_ARRAY : new GrExpression[]{rValue},
GrNamedArgument.EMPTY_ARRAY,
GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
expression
);
}
});
}
else {
writeSimpleBinaryExpression(expression.getOperationToken(), lValue, rValue);
}
}
}
private void writeAssignmentWithRefSetter(GrExpression ref, GrAssignmentExpression expression) {
GrExpression rValue = getRValue(expression);
if (PsiUtil.isExpressionUsed(expression)) {
LOG.assertTrue(context.getRefSetterName(expression) != null);
builder.append(context.getRefSetterName(expression)).append('(');
ref.accept(this);
builder.append(", ");
if (rValue != null) {
rValue.accept(this);
}
builder.append(')');
}
else {
ref.accept(this);
builder.append(".set(");
if (rValue != null) {
rValue.accept(this);
}
builder.append(')');
}
}
/**
* returns rValue for lValue = expr
* lValue+Rvalue for lValue += rValue
*/
@Nullable
private GrExpression getRValue(GrAssignmentExpression expression) {
GrExpression rValue = expression.getRValue();
if (rValue == null) return null;
GrExpression lValue = expression.getLValue();
IElementType opToken = expression.getOperationTokenType();
if (opToken == GroovyTokenTypes.mASSIGN) return rValue;
Pair<String, IElementType> pair = GenerationUtil.getBinaryOperatorType(opToken);
LOG.assertTrue(pair != null);
return factory.createExpressionFromText(lValue.getText() + pair.getFirst() + rValue.getText(), expression);
}
private void writeAssignmentWithSetter(GrExpression qualifier, PsiMethod setter, GrAssignmentExpression assignment) {
GrExpression rValue = getRValue(assignment);
LOG.assertTrue(rValue != null);
writeAssignmentWithSetter(qualifier, setter, new GrExpression[]{rValue}, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, PsiSubstitutor.EMPTY, assignment);
}
private void writeAssignmentWithSetter(@Nullable GrExpression qualifier,
@NotNull PsiMethod method,
@NotNull GrExpression[] exprs,
@NotNull GrNamedArgument[] namedArgs,
@NotNull GrClosableBlock[] closures,
@NotNull PsiSubstitutor substitutor,
@NotNull GrAssignmentExpression assignment) {
if (PsiUtil.isExpressionUsed(assignment)) {
final GrExpression rValue = assignment.getRValue();
//inline setter method invocation in case of tail statement and simple right value
if (PsiUtil.isExpressionStatement(assignment) && PsiUtil.isReturnStatement(assignment) && rValue != null && isVarAccess(rValue)) {
final StringBuilder assignmentBuffer = new StringBuilder();
new ExpressionGenerator(assignmentBuffer, context).invokeMethodOn(method, qualifier, exprs, namedArgs, closures, substitutor, assignment);
assignmentBuffer.append(';');
context.myStatements.add(assignmentBuffer.toString());
rValue.accept(this);
}
else {
String setterName = context.getSetterName(method, assignment);
GrExpression[] args;
if (method.hasModifierProperty(PsiModifier.STATIC)) {
args = exprs;
}
else {
args = new GrExpression[exprs.length + 1];
if (qualifier == null) {
qualifier = factory.createExpressionFromText("this", assignment);
}
args[0] = qualifier;
System.arraycopy(exprs, 0, args, 1, exprs.length);
}
GenerationUtil.invokeMethodByName(null, setterName, args, namedArgs, closures, this, assignment);
}
}
else {
invokeMethodOn(method, qualifier, exprs, namedArgs, closures, substitutor, assignment);
}
}
private static boolean isVarAccess(@Nullable GrExpression expr) {
if (expr instanceof GrReferenceExpression) {
final PsiElement resolved = ((GrReferenceExpression)expr).resolve();
if (resolved instanceof PsiVariable) {
return true;
}
}
return false;
}
@Override
public void visitBinaryExpression(GrBinaryExpression expression) {
final GrExpression left = expression.getLeftOperand();
GrExpression right = expression.getRightOperand();
final PsiType ltype = left.getType();
final PsiElement token = expression.getOperationToken();
final IElementType op = expression.getOperationTokenType();
if (op == GroovyTokenTypes.mREGEX_FIND) {
builder.append(GroovyCommonClassNames.JAVA_UTIL_REGEX_PATTERN).append(".compile(");
if (right != null) {
right.accept(this);
}
builder.append(").matcher(");
left.accept(this);
builder.append(')');
return;
}
if (op == GroovyTokenTypes.mREGEX_MATCH) {
builder.append(GroovyCommonClassNames.JAVA_UTIL_REGEX_PATTERN).append(".matches(");
if (right != null) {
right.accept(this);
}
builder.append(", ");
left.accept(this);
builder.append(')');
return;
}
if ((op == GroovyTokenTypes.mEQUAL || op == GroovyTokenTypes.mNOT_EQUAL) && (GrInspectionUtil.isNull(left) || right != null && GrInspectionUtil.isNull(right))) {
writeSimpleBinaryExpression(token, left, right);
return;
}
if (op == GroovyTokenTypes.kIN && right instanceof GrReferenceExpression && InheritanceUtil.isInheritor(right.getType(), CommonClassNames.JAVA_LANG_CLASS)) {
final PsiType type = com.intellij.psi.util.PsiUtil.substituteTypeParameter(right.getType(), CommonClassNames.JAVA_LANG_CLASS, 0, true);
writeInstanceof(left, type, expression);
return;
}
if (shouldNotReplaceOperatorWithMethod(ltype, right, op)) {
writeSimpleBinaryExpression(token, left, right);
return;
}
final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(expression.multiResolve(false));
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PsiMethod) {
if (right == null) {
right = factory.createExpressionFromText("null");
}
if (op == GroovyTokenTypes.mNOT_EQUAL && "equals".equals(((PsiMethod)resolved).getName())) {
builder.append('!');
}
invokeMethodOn(
((PsiMethod)resolved),
left,
new GrExpression[]{right},
GrNamedArgument.EMPTY_ARRAY,
GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
expression
);
if (op == GroovyTokenTypes.mGE) {
builder.append(" >= 0");
}
else if (op == GroovyTokenTypes.mGT) {
builder.append(" > 0");
}
else if (op == GroovyTokenTypes.mLT) {
builder.append(" < 0");
}
else if (op == GroovyTokenTypes.mLE) builder.append(" <= 0");
}
else {
writeSimpleBinaryExpression(token, left, right);
}
}
private static boolean shouldNotReplaceOperatorWithMethod(@Nullable PsiType ltype, @Nullable GrExpression right, IElementType op) {
if (!GenerationSettings.replaceOperatorsWithMethodsForNumbers) {
//adding something to string
if ((op == GroovyTokenTypes.mPLUS || op == GroovyTokenTypes.mPLUS_ASSIGN) && ltype != null && TypesUtil.isClassType(ltype, CommonClassNames.JAVA_LANG_STRING)) {
return true;
}
//we think it is number operation if we don't know right argument
if (TypesUtil.isNumericType(ltype) && (right == null || TypesUtil.isNumericType(right.getType()))) return true;
}
if (op == GroovyTokenTypes.mLNOT && isBooleanType(ltype)) {
return true;
}
return false;
}
private static boolean isBooleanType(PsiType type) {
return type == PsiType.BOOLEAN || type != null && type.equalsToText(CommonClassNames.JAVA_LANG_BOOLEAN);
}
private void writeSimpleBinaryExpression(PsiElement opToken, GrExpression left, GrExpression right) {
left.accept(this);
builder.append(' ');
builder.append(opToken.getText());
if (right != null) {
builder.append(' ');
right.accept(this);
}
}
@Override
public void visitUnaryExpression(GrUnaryExpression expression) {
final boolean postfix = expression.isPostfix();
final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(expression.multiResolve(false));
final PsiElement resolved = resolveResult.getElement();
final GrExpression operand = expression.getOperand();
IElementType opType = expression.getOperationTokenType();
if (resolved instanceof PsiMethod) {
if (opType == GroovyTokenTypes.mINC || opType == GroovyTokenTypes.mDEC) {
if (!postfix || expression.getParent() instanceof GrStatementOwner || expression.getParent() instanceof GrControlStatement) {
if (generatePrefixIncDec((PsiMethod)resolved, operand, expression)) return;
}
}
if (operand != null && shouldNotReplaceOperatorWithMethod(operand.getType(), null, expression.getOperationTokenType())) {
writeSimpleUnary(operand, expression, this);
}
else {
if (opType == GroovyTokenTypes.mLNOT) {
builder.append('!');
}
invokeMethodOn(
((PsiMethod)resolved),
operand,
GrExpression.EMPTY_ARRAY,
GrNamedArgument.EMPTY_ARRAY,
GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
expression
);
}
}
else if (operand != null) {
if (postfix) {
operand.accept(this);
builder.append(expression.getOperationToken().getText());
}
else {
builder.append(expression.getOperationToken().getText());
operand.accept(this);
}
}
}
private boolean generatePrefixIncDec(PsiMethod method,
GrExpression operand,
GrUnaryExpression unary) {
if (!(operand instanceof GrReferenceExpression)) return false;
final GrExpression qualifier = ((GrReferenceExpression)operand).getQualifier();
final GroovyResolveResult resolveResult = ((GrReferenceExpression)operand).advancedResolve();
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PsiMethod && GroovyPropertyUtils.isSimplePropertyGetter((PsiMethod)resolved)) {
final PsiMethod getter = (PsiMethod)resolved;
final String propertyName = GroovyPropertyUtils.getPropertyNameByGetter(getter);
final PsiType type;
if (qualifier == null) {
type = null;
}
else {
type = qualifier.getType();
if (type == null) return false;
}
final PsiMethod setter = GroovyPropertyUtils.findPropertySetter(type, propertyName, unary);
if (setter == null) return false;
final ExpressionGenerator generator = new ExpressionGenerator(new StringBuilder(), context);
if (shouldNotReplaceOperatorWithMethod(operand.getType(), null, unary.getOperationTokenType())) {
writeSimpleUnary(operand, unary, generator);
}
else {
generator.invokeMethodOn(
method,
operand,
GrExpression.EMPTY_ARRAY, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
unary
);
}
final GrExpression fromText = factory.createExpressionFromText(generator.toString(), unary);
invokeMethodOn(
setter,
qualifier,
new GrExpression[]{fromText}, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
unary
);
}
else if (resolved instanceof PsiVariable) {
boolean wrap = context.analyzedVars.toWrap((PsiVariable)resolved);
boolean doNeedExpression = PsiUtil.isExpressionUsed(unary);
StringBuilder curBuilder;
ExpressionGenerator curGenerator;
if (doNeedExpression && wrap) {
curBuilder = new StringBuilder();
curGenerator = new ExpressionGenerator(curBuilder, context);
}
else {
curBuilder = builder;
curGenerator = this;
}
boolean shouldInsertParentheses = !wrap && doNeedExpression;
if (shouldInsertParentheses) {
curBuilder.append('(');
}
operand.accept(curGenerator);
if (wrap) {
curBuilder.append(".set(");
}
else {
curBuilder.append(" = ");
}
if (shouldNotReplaceOperatorWithMethod(operand.getType(), null, unary.getOperationTokenType())) {
writeSimpleUnary((GrExpression)operand.copy(), unary, curGenerator);
}
else {
curGenerator.invokeMethodOn(
method,
(GrExpression)operand.copy(),
GrExpression.EMPTY_ARRAY, GrNamedArgument.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
unary
);
}
if (shouldInsertParentheses) {
curBuilder.append(')');
}
if (wrap) {
curBuilder.append(')');
if (doNeedExpression) {
curBuilder.append(';');
context.myStatements.add(curBuilder.toString());
operand.accept(this);
builder.append(".get()");
}
}
}
return true;
}
private static void writeSimpleUnary(GrExpression operand, GrUnaryExpression unary, ExpressionGenerator generator) {
String opTokenText = unary.getOperationToken().getText();
boolean isPostfix = unary.isPostfix();
if (!isPostfix) {
generator.getBuilder().append(opTokenText);
}
operand.accept(generator);
if (isPostfix) {
generator.getBuilder().append(opTokenText);
}
}
@Override
//doesn't visit GrString and regexps
public void visitLiteralExpression(GrLiteral literal) {
final TypeConstraint[] constraints = GroovyExpectedTypesProvider.calculateTypeConstraints(literal);
boolean isChar = false;
for (TypeConstraint constraint : constraints) {
if (constraint instanceof SubtypeConstraint && TypesUtil.unboxPrimitiveTypeWrapper(constraint.getDefaultType()) == PsiType.CHAR) {
isChar = true;
}
}
final String text = literal.getText();
if (text.startsWith("'''") || text.startsWith("\"\"\"")) {
String string = GrStringUtil.removeQuotes(text).replace("\n", "\\n").replace("\r", "\\r");
builder.append('"').append(string).append('"');
}
else if (text.startsWith("'")) {
if (isChar) {
builder.append(text);
}
else {
builder.append('"').append(StringUtil.escapeQuotes(StringUtil.trimEnd(text.substring(1, text.length()), "'"))).append('"');
}
}
else if (text.startsWith("\"")) {
if (isChar) {
builder.append('\'').append(StringUtil.escapeQuotes(StringUtil.trimEnd(text.substring(1, text.length()), "\""))).append('\'');
}
else {
builder.append(text);
}
}
else {
builder.append(text);
}
}
@Override
public void visitGStringExpression(GrString gstring) {
final String newExprText = ConvertGStringToStringIntention.convertGStringLiteralToStringLiteral(gstring);
final GrExpression newExpr = factory.createExpressionFromText(newExprText, gstring);
newExpr.accept(this);
}
@Override
public void visitReferenceExpression(GrReferenceExpression referenceExpression) {
final GrExpression qualifier = referenceExpression.getQualifier();
final GroovyResolveResult resolveResult = referenceExpression.advancedResolve();
final PsiElement resolved = resolveResult.getElement();
final String referenceName = referenceExpression.getReferenceName();
if (PsiUtil.isThisOrSuperRef(referenceExpression)) {
writeThisOrSuperRef(referenceExpression, qualifier, referenceName);
return;
}
if (ResolveUtil.isClassReference(referenceExpression)) {
LOG.assertTrue(qualifier != null);
qualifier.accept(this);
builder.append(".class");
return;
}
//class name used as expression. Should be converted to <className>.class
if (resolved instanceof PsiClass && PsiUtil.isExpressionUsed(referenceExpression)) {
builder.append(((PsiClass)resolved).getQualifiedName());
builder.append(".class");
return;
}
//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(referenceName)) {
builder.append(referenceName);
return;
}
//all refs in script that are not resolved are saved in 'binding' of the script
if (qualifier == null &&
(resolved == null ||
resolved instanceof GrBindingVariable ||
resolved instanceof LightElement && !(resolved instanceof ClosureSyntheticParameter)) &&
(referenceExpression.getParent() instanceof GrIndexProperty || !(referenceExpression.getParent() instanceof GrCall)) &&
PsiUtil.getContextClass(referenceExpression) instanceof GroovyScriptClass) {
final GrExpression thisExpr = factory.createExpressionFromText("this", referenceExpression);
thisExpr.accept(this);
builder.append(".getBinding().getProperty(\"").append(referenceExpression.getReferenceName()).append("\")");
return;
}
final IElementType type = referenceExpression.getDotTokenType();
GrExpression qualifierToUse = qualifier;
if (type == GroovyTokenTypes.mMEMBER_POINTER) {
LOG.assertTrue(qualifier != null);
builder.append("new ").append(GroovyCommonClassNames.ORG_CODEHAUS_GROOVY_RUNTIME_METHOD_CLOSURE).append('(');
qualifier.accept(this);
builder.append(", \"").append(referenceName).append("\")");
return;
}
if (type == GroovyTokenTypes.mOPTIONAL_DOT) {
LOG.assertTrue(qualifier != null);
String qualifierName = createVarByInitializer(qualifier);
builder.append('(').append(qualifierName).append(" == null ? null : ");
qualifierToUse = factory.createReferenceExpressionFromText(qualifierName, referenceExpression);
}
if (resolveResult.isInvokedOnProperty()) {
//property-style access to accessor (e.g. qual.prop should be translated to qual.getProp())
LOG.assertTrue(resolved instanceof PsiMethod);
LOG.assertTrue(GroovyPropertyUtils.isSimplePropertyGetter((PsiMethod)resolved));
invokeMethodOn(
((PsiMethod)resolved),
qualifierToUse,
GrExpression.EMPTY_ARRAY,
GrNamedArgument.EMPTY_ARRAY,
GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
referenceExpression
);
}
else {
if (qualifierToUse != null) {
qualifierToUse.accept(this);
builder.append('.');
}
if (resolved instanceof PsiNamedElement && !(resolved instanceof GrBindingVariable)) {
final String refName = ((PsiNamedElement)resolved).getName();
if (resolved instanceof GrVariable && context.analyzedVars.toWrap((GrVariable)resolved)) {
//this var should be wrapped by groovy.lang.Reference. so we add .get() tail.
builder.append(context.analyzedVars.toVarName((GrVariable)resolved));
if (!PsiUtil.isAccessedForWriting(referenceExpression)) {
builder.append(".get()");
}
}
else if (resolved instanceof PsiClass) {
TypeWriter.writeType(builder, referenceExpression.getType(), referenceExpression);
}
else {
builder.append(refName);
}
}
else {
//unresolved reference
if (referenceName != null) {
if (PsiUtil.isAccessedForWriting(referenceExpression)) {
builder.append(referenceName);
}
else {
PsiType stringType = PsiType.getJavaLangString(referenceExpression.getManager(), referenceExpression.getResolveScope());
PsiType qualifierType = PsiImplUtil.getQualifierType(referenceExpression);
GroovyResolveResult[] candidates = qualifierType != null
? ResolveUtil.getMethodCandidates(qualifierType, "getProperty", referenceExpression,
stringType)
: GroovyResolveResult.EMPTY_ARRAY;
final PsiElement method = PsiImplUtil.extractUniqueElement(candidates);
if (method != null) {
builder.append("getProperty(\"").append(referenceName).append("\")");
}
else {
builder.append(referenceName);
}
}
}
else {
final PsiElement nameElement = referenceExpression.getReferenceNameElement();
if (nameElement instanceof GrExpression) {
((GrExpression)nameElement).accept(this);
}
else if (nameElement != null) {
builder.append(nameElement.toString());
}
}
}
}
if (type == GroovyTokenTypes.mOPTIONAL_DOT) {
builder.append(')');
}
}
private void writeThisOrSuperRef(GrReferenceExpression referenceExpression, GrExpression qualifier, String referenceName) {
if (!context.isInAnonymousContext() && qualifier != null) {
qualifier.accept(this);
builder.append('.');
builder.append(referenceName);
}
else if (GenerationUtil.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;
}
GenerationUtil.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);
GenerationUtil.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();
if (inner != null) initializer = inner;
if (initializer instanceof GrReferenceExpression) {
final GrExpression qualifier = ((GrReferenceExpression)initializer).getQualifier();
if (qualifier == null) {
final PsiElement resolved = ((GrReferenceExpression)initializer).resolve();
if (resolved instanceof GrVariable && !(resolved instanceof GrField)) {
//don't create new var. it is already exists
return ((GrVariable)resolved).getName();
}
}
}
final String name = GenerationUtil.suggestVarName(initializer, context);
final StringBuilder builder = new StringBuilder();
builder.append("final ");
TypeWriter.writeType(builder, initializer.getType(), initializer);
builder.append(' ').append(name).append(" = ");
initializer.accept(new ExpressionGenerator(builder, context));
builder.append(';');
context.myStatements.add(builder.toString());
return name;
}
@Override
public void visitCastExpression(GrTypeCastExpression typeCastExpression) {
final GrTypeElement typeElement = typeCastExpression.getCastTypeElement();
final GrExpression operand = typeCastExpression.getOperand();
generateCast(typeElement, operand);
}
private void generateCast(GrTypeElement typeElement, GrExpression operand) {
builder.append('(');
TypeWriter.writeType(builder, typeElement.getType(), typeElement);
builder.append(')');
boolean insertParentheses =
operand instanceof GrBinaryExpression && ((GrBinaryExpression)operand).getOperationTokenType() == GroovyTokenTypes.mEQUAL;
if (insertParentheses) builder.append('(');
if (operand != null) {
operand.accept(this);
}
if (insertParentheses) builder.append(')');
}
@Override
public void visitSafeCastExpression(GrSafeCastExpression typeCastExpression) {
final GrExpression operand = (GrExpression)PsiUtil.skipParenthesesIfSensibly(typeCastExpression.getOperand(), false);
final GrTypeElement typeElement = typeCastExpression.getCastTypeElement();
if (operand instanceof GrListOrMap && ((GrListOrMap)operand).isMap() && typeElement != null) {
AnonymousFromMapGenerator.writeAnonymousMap((GrListOrMap)operand, typeElement, builder, context);
return;
}
final PsiType type = typeElement.getType();
if (operand instanceof GrListOrMap && !((GrListOrMap)operand).isMap() && type instanceof PsiArrayType) {
builder.append("new ");
final GrExpression[] initializers = ((GrListOrMap)operand).getInitializers();
if (initializers.length == 0) {
TypeWriter.writeTypeForNew(builder, ((PsiArrayType)type).getComponentType(), typeCastExpression);
builder.append("[0]");
}
else {
TypeWriter.writeTypeForNew(builder, type, typeCastExpression);
builder.append('{');
for (GrExpression initializer : initializers) {
initializer.accept(this);
builder.append(", ");
}
if (initializers.length > 0) {
builder.delete(builder.length() - 2, builder.length());
//builder.removeFromTheEnd(2);
}
builder.append('}');
}
return;
}
final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(typeCastExpression.multiResolve(false));
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PsiMethod) {
final GrExpression typeParam;
try {
typeParam = factory.createExpressionFromText(typeElement.getText(), typeCastExpression);
}
catch (IncorrectOperationException e) {
generateCast(typeElement, operand);
return;
}
invokeMethodOn(
((PsiMethod)resolved),
operand,
new GrExpression[]{typeParam},
GrNamedArgument.EMPTY_ARRAY,
GrClosableBlock.EMPTY_ARRAY,
resolveResult.getSubstitutor(),
typeCastExpression
);
}
else {
generateCast(typeElement, operand);
}
}
@Override
public void visitInstanceofExpression(GrInstanceOfExpression expression) {
final GrExpression operand = expression.getOperand();
final GrTypeElement typeElement = expression.getTypeElement();
writeInstanceof(operand, typeElement != null ? typeElement.getType() : null, expression);
}
private void writeInstanceof(@NotNull GrExpression operand, @Nullable PsiType type, @NotNull PsiElement context) {
operand.accept(this);
builder.append(" instanceof ");
if (type != null) {
TypeWriter.writeType(builder, type, context);
}
}
@Override
public void visitBuiltinTypeClassExpression(GrBuiltinTypeClassExpression expression) {
PsiElement firstChild = expression.getFirstChild();
LOG.assertTrue(firstChild != null);
ASTNode node = firstChild.getNode();
LOG.assertTrue(node != null);
final IElementType type = node.getElementType();
final String boxed = TypesUtil.getBoxedTypeName(type);
builder.append(boxed);
if (expression.getParent() instanceof GrIndexProperty) {
builder.append("[]");
}
builder.append(".class");
}
@Override
public void visitParenthesizedExpression(GrParenthesizedExpression expression) {
builder.append('(');
final GrExpression operand = expression.getOperand();
if (operand != null) {
operand.accept(this);
}
builder.append(')');
}
@Override
public void visitPropertySelection(GrPropertySelection expression) {
expression.getQualifier().accept(this);
builder.append('.');
builder.append(expression.getReferenceNameElement().getText());
}
@Override
public void visitIndexProperty(GrIndexProperty expression) {
final GrExpression selectedExpression = expression.getInvokedExpression();
final PsiType thisType = selectedExpression.getType();
final GrArgumentList argList = expression.getArgumentList();
if (argList.getAllArguments().length == 0) { // int[] or String[]
if (selectedExpression instanceof GrBuiltinTypeClassExpression) {
selectedExpression.accept(this);
return;
}
else if (selectedExpression instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression)selectedExpression).resolve();
if (resolved instanceof PsiClass) {
builder.append(((PsiClass)resolved).getQualifiedName());
builder.append("[].class");
return;
}
}
}
final PsiType[] argTypes = PsiUtil.getArgumentTypes(argList);
final GrExpression[] exprArgs = argList.getExpressionArguments();
final GrNamedArgument[] namedArgs = argList.getNamedArguments();
if (!PsiImplUtil.isSimpleArrayAccess(thisType, argTypes, expression, PsiUtil.isLValue(expression))) {
final GroovyResolveResult candidate = PsiImplUtil.extractUniqueResult(expression.multiResolve(false));
PsiElement element = candidate.getElement();
if (element != null || !PsiUtil.isLValue(expression)) { //see the case of l-value in assignment expression
if (element instanceof GrGdkMethod &&
((GrGdkMethod)element).getStaticMethod().getParameterList().getParameters()[0].getType().equalsToText("java.util.Map<K,V>")) {
PsiClass map = JavaPsiFacade.getInstance(context.project).findClass(CommonClassNames.JAVA_UTIL_MAP, expression.getResolveScope());
if (map != null) {
PsiMethod[] gets = map.findMethodsByName("get", false);
invokeMethodOn(gets[0], selectedExpression, exprArgs, namedArgs, GrClosableBlock.EMPTY_ARRAY, PsiSubstitutor.EMPTY, expression);
return;
}
}
else if (element instanceof GrGdkMethod &&
((GrGdkMethod)element).getStaticMethod().getParameterList().getParameters()[0].getType()
.equalsToText("java.util.List<T>")) {
PsiClass list =
JavaPsiFacade.getInstance(context.project).findClass(CommonClassNames.JAVA_UTIL_LIST, expression.getResolveScope());
if (list != null) {
PsiMethod[] gets = list.findMethodsByName("get", false);
invokeMethodOn(gets[0], selectedExpression, exprArgs, namedArgs, GrClosableBlock.EMPTY_ARRAY, PsiSubstitutor.EMPTY, expression);
return;
}
}
GenerationUtil
.invokeMethodByResolveResult(selectedExpression, candidate, "getAt", exprArgs, namedArgs, GrClosableBlock.EMPTY_ARRAY, this, expression);
return;
}
}
selectedExpression.accept(this);
builder.append('[');
final GrExpression arg = exprArgs[0];
arg.accept(this);
builder.append(']');
}
public void invokeMethodOn(@NotNull PsiMethod method,
@Nullable GrExpression caller,
@NotNull GrExpression[] exprs,
@NotNull GrNamedArgument[] namedArgs,
@NotNull GrClosableBlock[] closures,
@NotNull PsiSubstitutor substitutor,
@NotNull GroovyPsiElement context) {
if (method instanceof GrGdkMethod) {
if (CustomMethodInvocator.invokeMethodOn(this, (GrGdkMethod)method, caller, exprs, namedArgs, closures, substitutor, context)) return;
GrExpression[] newArgs = new GrExpression[exprs.length + 1];
System.arraycopy(exprs, 0, newArgs, 1, exprs.length);
if (method.hasModifierProperty(PsiModifier.STATIC)) {
newArgs[0] = factory.createExpressionFromText("null");
}
else {
if (caller == null) {
caller = factory.createExpressionFromText("this", context);
}
newArgs[0] = caller;
}
invokeMethodOn(((GrGdkMethod)method).getStaticMethod(), null, newArgs, namedArgs, closures, substitutor, context);
return;
}
if (method.hasModifierProperty(PsiModifier.STATIC) && caller == null) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && !PsiTreeUtil.isAncestor(containingClass, context, true)) {
builder.append(containingClass.getQualifiedName()).append('.');
}
}
else {
//LOG.assertTrue(caller != null, "instance method call should have caller");
if (caller != null) {
final boolean castNeeded = GenerationUtil.isCastNeeded(caller, method, this.context);
if (castNeeded) {
writeCastForMethod(caller, method, context);
}
caller.accept(this);
if (castNeeded) {
builder.append(')');
}
builder.append('.');
}
}
builder.append(method.getName());
final GrClosureSignature signature = GrClosureSignatureUtil.createSignature(method, substitutor);
new ArgumentListGenerator(builder, this.context).generate(signature, exprs, namedArgs, closures, context);
}
private void writeCastForMethod(@NotNull GrExpression caller, @NotNull PsiMethod method, @NotNull GroovyPsiElement context) {
final PsiType type = inferCastType(caller, method, context);
if (type == null) return;
builder.append('(');
builder.append('(');
TypeWriter.writeType(builder, type, context);
builder.append(')');
}
@Nullable
private static PsiType inferCastType(@NotNull GrExpression caller, @NotNull PsiMethod method, @NotNull GroovyPsiElement context) {
final PsiType type = caller.getType();
if (type instanceof PsiIntersectionType) {
final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
for (PsiType conjunct : conjuncts) {
final GenerationUtil.CheckProcessElement processor = new GenerationUtil.CheckProcessElement(method);
ResolveUtil.processAllDeclarationsSeparately(conjunct, processor, new BaseScopeProcessor() {
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
return false;
}
}, ResolveState.initial(), context);
if (processor.isFound()) return conjunct;
}
}
return type;
}
@Override
public void visitListOrMap(GrListOrMap listOrMap) {
final PsiType type = listOrMap.getType();
//can be PsiArrayType or GrLiteralClassType
LOG.assertTrue(type instanceof GrLiteralClassType || type instanceof PsiArrayType || type instanceof PsiClassType);
if (listOrMap.isMap()) {
if (listOrMap.getNamedArguments().length == 0) {
builder.append("new ");
TypeWriter.writeTypeForNew(builder, type, listOrMap);
builder.append("()");
}
else {
String varName = generateMapVariableDeclaration(listOrMap, type);
generateMapElementInsertions(listOrMap, varName);
builder.append(varName);
}
}
else {
builder.append("new ");
PsiType typeToUse = getTypeToUseByList(listOrMap, type);
TypeWriter.writeTypeForNew(builder, typeToUse, listOrMap);
if (typeToUse instanceof PsiArrayType) {
if (listOrMap.getInitializers().length == 0) {
builder.replace(builder.length() - 2, builder.length(), "[0]");
}
else {
builder.append('{');
genInitializers(listOrMap);
builder.append('}');
}
}
else if (listOrMap.getInitializers().length == 0) {
builder.append("()");
}
else {
builder.append("(java.util.Arrays.asList(");
genInitializers(listOrMap);
builder.append("))");
}
}
}
private static PsiType getTypeToUseByList(GrListOrMap listOrMap, PsiType type) {
if (isImplicitlyCastedToArray(listOrMap)) {
PsiType iterable = ClosureParameterEnhancer.findTypeForIteration(listOrMap, listOrMap);
if (iterable != null) {
return new PsiArrayType(iterable);
}
}
return type;
}
private static boolean isImplicitlyCastedToArray(GrListOrMap list) {
PsiElement parent = list.getParent();
GrControlFlowOwner owner = ControlFlowUtils.findControlFlowOwner(list);
if (!(owner instanceof GrOpenBlock && owner.getParent() instanceof GrMethod)) return false;
if (!(parent instanceof GrReturnStatement || ControlFlowUtils.isReturnValue(list, owner))) return false;
PsiType type = ((GrMethod)owner.getParent()).getReturnType();
return type instanceof PsiArrayType;
}
private void generateMapElementInsertions(GrListOrMap listOrMap, String varName) {
for (GrNamedArgument arg : listOrMap.getNamedArguments()) {
StringBuilder insertion = new StringBuilder();
insertion.append(varName).append(".put(");
final String stringKey = arg.getLabelName();
if (stringKey != null) {
insertion.append('"').append(stringKey).append('"');
}
else {
final GrArgumentLabel label = arg.getLabel();
final GrExpression expression = label == null ? null : label.getExpression();
if (expression != null) {
expression.accept(new ExpressionGenerator(insertion, context));
}
else {
//todo should we generate an exception?
}
}
insertion.append(", ");
final GrExpression expression = arg.getExpression();
if (expression != null) {
expression.accept(new ExpressionGenerator(insertion, context));
}
else {
//todo should we generate an exception?
}
insertion.append(");");
context.myStatements.add(insertion.toString());
}
}
private String generateMapVariableDeclaration(GrListOrMap listOrMap, PsiType type) {
StringBuilder declaration = new StringBuilder();
TypeWriter.writeType(declaration, type, listOrMap);
final String varName = GenerationUtil.suggestVarName(type, listOrMap, this.context);
declaration.append(' ').append(varName).append(" = new ");
TypeWriter.writeTypeForNew(declaration, type, listOrMap);
declaration.append('(');
//insert count of elements in list or map
declaration.append(listOrMap.getNamedArguments().length);
declaration.append(");");
context.myStatements.add(declaration.toString());
return varName;
}
private void genInitializers(GrListOrMap list) {
LOG.assertTrue(!list.isMap());
final GrExpression[] initializers = list.getInitializers();
for (GrExpression expr : initializers) {
expr.accept(this);
builder.append(", ");
}
if (initializers.length > 0) {
builder.delete(builder.length() - 2, builder.length());
//builder.removeFromTheEnd(2);
}
}
@Override
public void visitRangeExpression(GrRangeExpression range) {
final PsiType type = range.getType();
LOG.assertTrue(type instanceof GrRangeType);
final PsiClass resolved = ((GrRangeType)type).resolve();
builder.append("new ");
if (resolved == null) {
builder.append(GroovyCommonClassNames.GROOVY_LANG_OBJECT_RANGE);
}
else {
builder.append(resolved.getQualifiedName());
}
builder.append('(');
final GrExpression left = range.getLeftOperand();
left.accept(this);
builder.append(", ");
final GrExpression right = range.getRightOperand();
if (right != null) {
right.accept(this);
}
builder.append(')');
}
}