blob: a2a6d38d29c6e95f0c9a8703f58661d7625f4433 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.codehaus.groovy.ast.expr;
import org.codehaus.groovy.ast.*;
/**
* A method call on an object or class
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
*/
public class MethodCallExpression extends Expression implements MethodCall {
private Expression objectExpression;
private Expression method;
private Expression arguments;
private boolean spreadSafe = false;
private boolean safe = false;
private boolean implicitThis;
// type spec for generics
private GenericsType[] genericsTypes = null;
private boolean usesGenerics = false;
private MethodNode target;
public static final Expression NO_ARGUMENTS = new TupleExpression();
public MethodCallExpression(Expression objectExpression, String method, Expression arguments) {
this(objectExpression,new ConstantExpression(method),arguments);
}
public MethodCallExpression(Expression objectExpression, Expression method, Expression arguments) {
this.objectExpression = objectExpression;
this.method = method;
if (!(arguments instanceof TupleExpression)){
this.arguments = new TupleExpression(arguments);
} else {
this.arguments = arguments;
}
//TODO: set correct type here
// if setting type and a methodcall is the last expression in a method,
// then the method will return null if the method itself is not void too!
// (in bytecode after call: aconst_null, areturn)
this.setType(ClassHelper.DYNAMIC_TYPE);
this.setImplicitThis(true);
}
public void visit(GroovyCodeVisitor visitor) {
visitor.visitMethodCallExpression(this);
}
public Expression transformExpression(ExpressionTransformer transformer) {
MethodCallExpression answer =
new MethodCallExpression(transformer.transform(objectExpression), transformer.transform(method), transformer.transform(arguments));
answer.setSafe(safe);
answer.setSpreadSafe(spreadSafe);
answer.setImplicitThis(implicitThis);
answer.setSourcePosition(this);
answer.setMethodTarget(target);
answer.copyNodeMetaData(this);
return answer;
}
public Expression getArguments() {
return arguments;
}
public void setArguments(Expression arguments) {
if (!(arguments instanceof TupleExpression)){
this.arguments = new TupleExpression(arguments);
} else {
this.arguments = arguments;
}
}
public Expression getMethod() {
return method;
}
public void setMethod(Expression method) {
this.method = method;
}
public ASTNode getReceiver() {
return getObjectExpression();
}
/**
* This method returns the method name as String if it is no dynamic
* calculated method name, but a constant.
*/
public String getMethodAsString() {
if (! (method instanceof ConstantExpression)) return null;
ConstantExpression constant = (ConstantExpression) method;
return constant.getText();
}
public void setObjectExpression(Expression objectExpression) {
this.objectExpression = objectExpression;
}
public Expression getObjectExpression() {
return objectExpression;
}
public String getText() {
String object = objectExpression.getText();
String meth = method.getText();
String args = arguments.getText();
String spread = spreadSafe ? "*" : "";
String dereference = safe ? "?" : "";
return object + spread + dereference + "." + meth + args;
}
/**
* @return is this a safe method call, i.e. if true then if the source object is null
* then this method call will return null rather than throwing a null pointer exception
*/
public boolean isSafe() {
return safe;
}
public void setSafe(boolean safe) {
this.safe = safe;
}
public boolean isSpreadSafe() {
return spreadSafe;
}
public void setSpreadSafe(boolean value) {
spreadSafe = value;
}
/**
* @return true if no object expression was specified otherwise if
* some expression was specified for the object on which to evaluate
* the method then return false
*/
public boolean isImplicitThis() {
return implicitThis;
}
public void setImplicitThis(boolean implicitThis) {
this.implicitThis = implicitThis;
}
public String toString() {
return super.toString()
+ "[object: "
+ objectExpression
+ " method: "
+ method
+ " arguments: "
+ arguments
+ "]";
}
public GenericsType[] getGenericsTypes() {
return genericsTypes;
}
public void setGenericsTypes(GenericsType[] genericsTypes) {
usesGenerics = usesGenerics || genericsTypes != null;
this.genericsTypes = genericsTypes;
}
public boolean isUsingGenerics() {
return usesGenerics;
}
/**
* Sets a method call target for a direct method call.
* WARNING: A method call made this way will run outside of the MOP!
* @param mn the target as MethodNode, mn==null means no target
*/
public void setMethodTarget(MethodNode mn) {
this.target = mn;
if (mn!=null) {
setType(target.getReturnType());
} else {
setType(ClassHelper.OBJECT_TYPE);
}
}
/**
* @return the target as method node if set
*/
public MethodNode getMethodTarget() {
return target;
}
}