/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser.expressions;
import gw.internal.gosu.parser.*;
import gw.lang.parser.IExpression;
import gw.lang.parser.StandardCoercionManager;
import gw.lang.parser.expressions.IInitializerExpression;
import gw.lang.parser.expressions.INewExpression;
import gw.lang.parser.expressions.ITypeLiteralExpression;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
/**
* The 'new' operator as an expression:
* <pre>
* <i>new-expression</i>
* <b>new</b> <type-expression> <b>(</b> [<argument-list>] <b>)</b> [ <b>{...}</b> ]
* <b>new</b> <type-expression> <b>[</b> <expression> <b>]</b>
* <b>new</b> <type-expression> <b>[</b><b>]</b> <b>{</b> [<array-value-list>] <b>}</b>
* </pre>
*
* @see gw.lang.parser.IGosuParser
*/
public class NewExpression extends Expression implements INewExpression
{
private IType[] _argTypes;
private Expression[] _args;
private IConstructorInfo _constructor;
private List<Expression> _valueExpressions;
private List<Expression> _sizeExpressions;
private int _iArgPos;
private ITypeLiteralExpression _typeLiteral;
private IInitializerExpression _initExpr;
private boolean _anonymous;
private int[] _namedArgOrder;
/**
* Constructs a BeanMethodCallExpression given an ISymbolTable instance.
*/
public NewExpression()
{
}
/**
* @return An array of IType for the arguments of the method call.
*/
public IType[] getArgTypes()
{
return _argTypes;
}
/**
* @param argTypes An array of IType for the arguments of the method call.
*/
public void setArgTypes( IType... argTypes )
{
_argTypes = argTypes;
}
/**
* @return An array of expressions for corresponding to the arguments in the
* expression.
*/
public Expression[] getArgs()
{
return _args;
}
/**
* @param args An array of expressions for corresponding to the arguments in
* the expression.
*/
public void setArgs( Expression[] args )
{
_args = args;
}
public IConstructorInfo getConstructor()
{
return _constructor;
}
public int getArgPosition()
{
return _iArgPos;
}
public void setArgPosition( int iArgPos )
{
_iArgPos = iArgPos;
}
/**
* The constructor for the new operation.
* <p/>
* The properties: Constructor, ValueExpressions, and SizeExpression are
* mutually exclusive.
*/
public void setConstructor( IConstructorInfo constructor )
{
_constructor = constructor;
}
/**
* The value expression for the new array operation.
* <p/>
* The properties: Constructor, ValueExpressions, and SizeExpression are
* mutually exclusive.
*/
public void setValueExpressions( List<Expression> valueExpressions )
{
_valueExpressions = valueExpressions;
}
public List<Expression> getValueExpressions()
{
return _valueExpressions;
}
public void setInitializer( IInitializerExpression initializerExpression )
{
_initExpr = initializerExpression;
}
public IInitializerExpression getInitializer()
{
return _initExpr;
}
/**
* The size expression for the new array operation.
* <p/>
* The properties: Constructor, ValueExpressions, and SizeExpression are
* mutually exclusive.
*/
public void addSizeExpression( Expression sizeExpression )
{
if( _sizeExpressions == null )
{
_sizeExpressions = new ArrayList<Expression>( 2 );
}
_sizeExpressions.add( sizeExpression );
}
public List<Expression> getSizeExpressions()
{
return _sizeExpressions;
}
public boolean isCompileTimeConstant()
{
IInitializerExpression initializer = getInitializer();
if( initializer != null && initializer.isCompileTimeConstant() )
{
// Transform collection initialization to array constant
return true;
}
if( getValueExpressions() != null )
{
// Handle Java-style array initialization
for( Expression v : getValueExpressions() )
{
if( !v.isCompileTimeConstant() )
{
return false;
}
}
return true;
}
if( getSizeExpressions() != null )
{
for( Expression s : getSizeExpressions() )
{
if( !s.isCompileTimeConstant() )
{
return false;
}
}
return true;
}
if( getType().isArray() )
{
return true;
}
// Handle annotations as arguments
return JavaTypes.ANNOTATION().isAssignableFrom( getType() );
}
public Object evaluate()
{
if( !isCompileTimeConstant() )
{
return super.evaluate();
}
IInitializerExpression initializer = getInitializer();
if( initializer != null )
{
return getInitializer().evaluate();
}
if( _valueExpressions != null )
{
// Convert to an array for compile-time constant e.g., {a,b,c} is legal array expr for Annotation args
Class<?> arrayClass = getArrayClass(getType().getComponentType());
List<Expression> values = _valueExpressions;
Object instance = Array.newInstance(arrayClass.getComponentType(), values.size());
for( int i = 0; i < values.size(); i++ )
{
IExpression expr = values.get( i );
Array.set( instance, i, expr.evaluate() );
}
return instance;
}
if( _sizeExpressions != null )
{
Class<?> arrayClass = getArrayClass(getType().getComponentType());
return Array.newInstance(arrayClass.getComponentType(), ((Number)_sizeExpressions.get( 0 ).evaluate()).intValue() );
}
if( JavaTypes.ANNOTATION().isAssignableFrom( getType() ) )
{
ParseTree loc = getLocation();
boolean bProbablyFromACrappyTest = getGosuClass() == null;
return new GosuAnnotationInfo( new GosuAnnotation( (ICompilableTypeInternal)getGosuClass(), getType(), this, bProbablyFromACrappyTest ? 0 : loc.getOffset(), bProbablyFromACrappyTest ? -1 : loc.getExtent()+1 ),
getGosuClass() == null ? null : getGosuClass().getTypeInfo(), (IGosuClassInternal)getGosuClass(), 0 );
}
if( getType().isArray() )
{
Class<?> arrayClass = getArrayClass(getType().getComponentType());
return Array.newInstance(arrayClass.getComponentType(), 0);
}
throw new UnsupportedOperationException( "Cannot evaluate new-expression as compile-time constant value." );
}
private Class<?> getArrayClass(IType type) {
return Array.newInstance(getComponentClass(type), 0).getClass();
}
private Class<?> getComponentClass(IType type) {
if( StandardCoercionManager.isBoxed(type) )
{
type = TypeSystem.getPrimitiveType(type);
}
else if( type.isEnum() )
{
// An enum evaluates as the name of the enum constant field (for compile-time constant evaluation)
type = JavaTypes.STRING();
}
else if( JavaTypes.ANNOTATION().isAssignableFrom( type ) )
{
type = TypeSystem.get( IAnnotationInfo.class );
}
else if( JavaTypes.CLASS().isAssignableFrom( type ) )
{
type = JavaTypes.ITYPE();
}
else if( !type.isPrimitive() &&
type != JavaTypes.STRING() &&
!TypeSystem.get( IAnnotationInfo.class ).isAssignableFrom( type ) &&
!JavaTypes.CLASS().isAssignableFrom( type ) )
{
throw new IllegalStateException( " Illegal type: " + type.getName() + " A compile-time constant expression must be either primitive, String, Class, or Enum." );
}
Class<?> cls = ((IJavaType)type).getBackingClass();
cls = cls != null ? cls : getClassForRareCaseWhenRunningIJEditorProjectWhereGosuCoreJavaTypesAreSourceBased( type );
return cls;
}
private Class<?> getClassForRareCaseWhenRunningIJEditorProjectWhereGosuCoreJavaTypesAreSourceBased( IType type ) {
String fqn = type.getName();
Class<?> cls = Primitives.get( fqn );
if( cls == null ) {
try {
cls = Class.forName( fqn, false, getClass().getClassLoader() );
}
catch( ClassNotFoundException e ) {
throw new RuntimeException( e );
}
}
return cls;
}
@Override
public String toString()
{
StringBuilder strOut = new StringBuilder( "new " );
if( _constructor != null )
{
strOut.append( getType().getName() ).append( "(" );
if( _args != null && _args.length > 0 )
{
strOut.append( " " );
for( int i = 0; i < _args.length; i++ )
{
if( i != 0 )
{
strOut.append( ", " );
}
strOut.append( _args[i].toString() );
}
strOut.append( " " );
}
strOut.append( ")" );
if (_initExpr != null) {
strOut.append(_initExpr.toString());
}
return strOut.toString();
}
else if( _valueExpressions != null )
{
strOut.append( getType().getName() ).append( " {" );
for( int i = 0; i < _valueExpressions.size(); i++ )
{
if( i != 0 )
{
strOut.append( ", " );
}
strOut.append( _valueExpressions.get( i ).toString() );
}
return strOut.append( "}" ).toString();
}
else if( _sizeExpressions != null )
{
IType type = getType();
int iDims = 0;
do
{
iDims++;
type = type.getComponentType();
} while( type.isArray() );
strOut.append( type.getName() );
for( int i = 0; i < iDims; i++ )
{
strOut.append( '[' );
if( _sizeExpressions.size() > i )
{
strOut.append( _sizeExpressions.get( i ).toString() );
}
strOut.append( ']' );
}
return strOut.toString();
}
else
{
if (getType() != null && getType().getComponentType() != null) {
strOut.append( getType().getComponentType().getName() ).append( "[]{}" );
}
return strOut.toString();
}
}
public boolean isAnonymousClass()
{
return _anonymous;
}
public void setAnonymousClass( boolean anonymous )
{
_anonymous = anonymous;
}
public ITypeLiteralExpression getTypeLiteral() {
return _typeLiteral;
}
public void setTypeLiteral(ITypeLiteralExpression typeLiteral) {
_typeLiteral = typeLiteral;
}
public int[] getNamedArgOrder()
{
return _namedArgOrder;
}
public void setNamedArgOrder( int[] namedArgOrder )
{
_namedArgOrder = namedArgOrder;
}
}