/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.expressions; import gw.internal.gosu.parser.Expression; import gw.internal.gosu.parser.TypeLoaderAccess; import gw.internal.gosu.parser.TypeLord; import gw.internal.gosu.parser.Statement; import gw.internal.gosu.parser.statements.MemberAssignmentStatement; import gw.internal.gosu.parser.statements.ArrayAssignmentStatement; import gw.internal.gosu.parser.GosuParser; import gw.internal.gosu.parser.CannotExecuteGosuException; import gw.lang.parser.GosuParserTypes; import gw.lang.parser.IParsedElement; import gw.lang.parser.IExpression; import gw.lang.parser.expressions.IArrayAccessExpression; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IType; import gw.lang.reflect.IAnnotationInfo; import gw.lang.reflect.java.JavaTypes; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * Represents a member access expression in the Gosu grammar: * <pre> * <i>array-access</i> * <array-reference> [ <member> ] * <p/> * <i>array-reference</i> * <expression> * <p/> * <i>member</i> * <array-access> * <expression> * </pre> * * @see gw.lang.parser.IGosuParser */ public final class ArrayAccess extends Expression implements IArrayAccessExpression { /** The array expression */ private Expression _rootExpression; /** An expression for accessing a bean member/property dynamically */ private Expression _memberExpression; private boolean _bNullSafe; public ArrayAccess() { } public Expression getRootExpression() { return _rootExpression; } public void setRootExpression( Expression rootExpression ) { _rootExpression = rootExpression; IType rootType = _rootExpression.getType(); setTypeInternal( rootType ); } public Expression getMemberExpression() { return _memberExpression; } public void setMemberExpression( Expression memberExpression ) { _memberExpression = memberExpression; } public boolean isNullSafe() { return _bNullSafe; } public void setNullSafe( boolean bNullSafe ) { _bNullSafe = bNullSafe; } /** * Evaluates the expression. */ public Object evaluate() { if( !isCompileTimeConstant() ) { //## todo: maybe make this expression evaluate directly, making a class for this seems a bit much return super.evaluate(); } throw new CannotExecuteGosuException(); } public static IType getTypeToAutoInsert( IExpression rootExpression ) { IType featureType = ((MemberAccess)rootExpression).getPropertyInfo().getFeatureType().getTypeParameters()[0]; if( (featureType.isAbstract() || featureType.isInterface()) ) { IType concreteType = GosuParser.findImpl( featureType ); if( concreteType != null ) { featureType = concreteType; } } return featureType; } public static boolean needsAutoinsert( ArrayAccess arrayAccess ) { Expression rootExpr = arrayAccess.getRootExpression(); // if the root is not a list member access, we do not auto insert if( !(rootExpr instanceof MemberAccess) || !JavaTypes.LIST().isAssignableFrom(rootExpr.getType())) { return false; } // check if the root has the Autoinsert annotation MemberAccess memberAccess = (MemberAccess)rootExpr; IType autoinsertAnnotationType = JavaTypes.AUTOINSERT(); List<IAnnotationInfo> list = autoinsertAnnotationType == null ? null : memberAccess.getPropertyInfo().getAnnotationsOfType( autoinsertAnnotationType ); if( list == null || list.isEmpty() ) { return false; } IParsedElement parent = arrayAccess.getParent(); boolean bNestedInLhs = false; boolean bNested = true; while( bNested && (parent != null) && !(parent instanceof Statement) ) { bNested = parent instanceof MemberAccess || parent instanceof ArrayAccess; parent = parent.getParent(); } if( bNested ) { IParsedElement lhsRoot = null; if( parent instanceof MemberAssignmentStatement ) { lhsRoot = ((MemberAssignmentStatement)parent).getRootExpression(); } else if ( parent instanceof ArrayAssignmentStatement ) { lhsRoot = ((ArrayAssignmentStatement)parent).getArrayAccessExpression(); } IParsedElement csr = arrayAccess; while( csr instanceof Expression ) { if( lhsRoot == csr ) { bNestedInLhs = true; break; } csr = csr.getParent(); } } return bNestedInLhs; } public IType getComponentType() { return getType(); } @Override public String toString() { return getRootExpression().toString() + "[" + getMemberExpression().toString() + "]"; } @SuppressWarnings({"UnusedDeclaration"}) public static Object getArrayElement( Object obj, int iIndex, boolean bNullSafe ) { if( obj == null ) { if( bNullSafe ) { return null; } else { throw new NullPointerException(); } } IType objType = TypeLoaderAccess.instance().getIntrinsicTypeFromObject( obj ); if( objType.isArray() ) { return objType.getArrayComponent( obj, iIndex ); } if( obj instanceof CharSequence ) { return String.valueOf( ((CharSequence)obj).charAt( iIndex ) ); } if( obj instanceof List ) { return ((List)obj).get( iIndex ); } if( obj instanceof Collection ) { Iterator iter = ((Collection)obj).iterator(); return getElementFromIterator( iter, iIndex ); } if( obj instanceof Iterator ) { Iterator iter = (Iterator)obj; return getElementFromIterator( iter, iIndex ); } return null; } private void setTypeInternal( IType rootType ) { if( rootType.isArray() ) { setType( rootType.getComponentType() ); } else if( rootType == GosuParserTypes.STRING_TYPE() ) { setType( GosuParserTypes.STRING_TYPE() ); } else if( JavaTypes.COLLECTION().isAssignableFrom(rootType) ) { IType paramedType = TypeLord.findParameterizedType(rootType, JavaTypes.COLLECTION()); if( paramedType != null ) { setType( paramedType.getTypeParameters()[0] ); } else { setType( JavaTypes.OBJECT() ); } } else if( JavaTypes.ITERATOR().isAssignableFrom(rootType) ) { IType paramedType = TypeLord.findParameterizedType(rootType, JavaTypes.ITERATOR()); if( paramedType != null ) { setType( paramedType.getTypeParameters()[0] ); } else { setType( JavaTypes.OBJECT() ); } } else if( rootType instanceof IPlaceholder && ((IPlaceholder)rootType).isPlaceholder() ) { setType( rootType.getComponentType() ); } else { setType( JavaTypes.OBJECT() ); } } public static boolean supportsArrayAccess( IType type ) { return type.isArray() || (JavaTypes.LIST().isAssignableFrom(type) && !JavaTypes.LINKED_LIST().isAssignableFrom( type )) || JavaTypes.CHAR_SEQUENCE().isAssignableFrom(type) || (type instanceof IPlaceholder && ((IPlaceholder)type).isPlaceholder()); } private static Object getElementFromIterator( Iterator iter, int iIndex ) { int iCount = 0; while( iter.hasNext() ) { Object elem = iter.next(); if( iCount++ == iIndex ) { return elem; } } return null; } }