/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.parser.exceptions.ErrantGosuClassException;
import gw.lang.parser.EvaluationException;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.*;
import gw.lang.ir.IRType;
import gw.internal.gosu.ir.transform.util.NameResolver;
import gw.internal.gosu.ir.transform.AbstractElementTransformer;
import gw.internal.gosu.ir.nodes.IRPropertyFactory;
import gw.internal.gosu.ir.nodes.IRMethod;
import gw.util.GosuExceptionUtil;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IPresentationInfo;
import gw.lang.reflect.IPropertyAccessor;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.IGosuPropertyInfo;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
*/
public class GosuPropertyInfo extends GosuBaseAttributedFeatureInfo implements IGosuPropertyInfo
{
private ReducedDynamicPropertySymbol _dps;
private IType _type;
private IPropertyAccessor _accessor;
private boolean _bReadable;
public GosuPropertyInfo( IFeatureInfo container, DynamicPropertySymbol dps )
{
super( container );
_dps = (ReducedDynamicPropertySymbol) dps.createReducedSymbol();
_bReadable = _dps.isReadable();
((GosuClassTypeInfo)getOwnersType().getTypeInfo()).setModifierInfo( this, dps.getModifierInfo() );
}
public String getName()
{
return _dps.getName();
}
public String getDisplayName()
{
return _dps.getDisplayName();
}
public String getShortDescription()
{
return _dps.getDisplayName();
}
public String getDescription()
{
return _dps.getFullDescription();
}
public boolean isStatic()
{
return _dps.isStatic();
}
public boolean isPrivate()
{
return _dps.isPrivate();
}
public boolean isInternal()
{
return _dps.isInternal();
}
public boolean isProtected()
{
return _dps.isProtected();
}
public boolean isPublic()
{
return _dps.isPublic();
}
public boolean isAbstract()
{
return _dps.isAbstract();
}
public boolean isFinal()
{
return _dps.isFinal();
}
@Override
protected List<IGosuAnnotation> getGosuAnnotations()
{
return _dps.getAnnotations();
}
public boolean isReadable()
{
return _bReadable;
}
public boolean isWritable(IType whosAskin) {
if (_dps.isWritable()) {
IRelativeTypeInfo.Accessibility accessibilityForType = ((IRelativeTypeInfo) getContainer()).getAccessibilityForType(whosAskin);
boolean isAccessible = false;
ReducedDynamicFunctionSymbol setter = _dps.getSetterDfs();
switch (accessibilityForType) {
case PUBLIC:
if (setter.isPublic()) {
isAccessible = true;
}
break;
case PROTECTED:
if (setter.isPublic() || setter.isProtected()) {
isAccessible = true;
}
break;
case INTERNAL:
if (setter.isPublic() || setter.isInternal() || setter.isProtected()) {
isAccessible = true;
}
break;
case PRIVATE:
if (setter.isPublic() || setter.isInternal() || setter.isProtected() || setter.isPrivate()) {
isAccessible = true;
}
break;
}
return isAccessible;
}
return false;
}
public boolean isWritable()
{
return isWritable(null);
}
public IPropertyAccessor getAccessor()
{
if( _accessor == null )
{
IGosuClassInternal gsClass = getGosuClass();
if( !gsClass.isValid() )
{
throw new ErrantGosuClassException( gsClass );
}
_accessor = new GosuPropertyAccessor();
}
return _accessor;
}
public IPresentationInfo getPresentationInfo()
{
return IPresentationInfo.Default.GET;
}
IGosuClassInternal getGosuClass()
{
return getOwnersType();
}
public IType getFeatureType()
{
if( _type == null )
{
_type = getActualTypeInContainer( this, _dps.getType() );
}
return _type;
}
public ReducedDynamicPropertySymbol getDps()
{
return _dps;
}
@Override
public IType getContainingType() {
return getGosuClass();
}
public boolean equals( Object o )
{
if( this == o )
{
return true;
}
if( o == null || getClass() != o.getClass() )
{
return false;
}
GosuPropertyInfo that = (GosuPropertyInfo)o;
return getName().equals( that.getName() );
}
public int hashCode()
{
return getName().hashCode();
}
public GenericTypeVariable[] getTypeVariables()
{
return GenericTypeVariable.EMPTY_TYPEVARS;
}
public IType getParameterizedReturnType( IType... typeParams )
{
return null;
}
public IType[] getParameterizedParameterTypes( IType... typeParams )
{
return IType.EMPTY_ARRAY;
}
public IType[] getParameterizedParameterTypes2( IGosuClass ownersType, IType... typeParams )
{
return IType.EMPTY_ARRAY;
}
public TypeVarToTypeMap inferTypeParametersFromArgumentTypes( IType... argTypes )
{
return null;
}
@Override
public TypeVarToTypeMap inferTypeParametersFromArgumentTypes2( IGosuClass owningParameterizedType, IType... argTypes )
{
return null;
}
//----------------------------------------------------------------------------
// -- private methods --
private class GosuPropertyAccessor implements IPropertyAccessor
{
public Object getValue( Object ctx )
{
String methodName = NameResolver.getGetterNameForDPS( _dps );
try
{
Object[] args;
if( AbstractElementTransformer.requiresImplicitEnhancementArg( _dps.getGetterDfs() ) )
{
IGosuEnhancementInternal enhancement = (IGosuEnhancementInternal)_dps.getGetterDfs().getGosuClass();
List<Object> argList = new ArrayList<Object>();
argList.add( ctx );
if(enhancement.isParameterizedType() )
{
IType[] parameters = enhancement.getTypeParameters();
for( IType parameter : parameters )
{
argList.add( parameter );
}
}
else
{
IGenericTypeVariable[] typeVariables = enhancement.getGenericTypeVariables();
for( IGenericTypeVariable typeVariable : typeVariables )
{
argList.add( typeVariable.getBoundingType() );
}
}
args = argList.toArray( new Object[argList.size()] );
}
else
{
args = new Object[0];
}
IRMethod getterMethod = IRPropertyFactory.createIRProperty(GosuPropertyInfo.this).getGetterMethod();
List<IRType> allParameterTypes = getterMethod.getAllParameterTypes();
Class[] paramClasses = new Class[allParameterTypes.size()];
for (int i = 0; i < allParameterTypes.size(); i++) {
paramClasses[i] = allParameterTypes.get(i).getJavaClass();
}
Method method = GosuMethodInfo.getMethod( getOwnersType().getBackingClass(), methodName, paramClasses );
return method.invoke( ctx, args );
}
catch( IllegalAccessException e )
{
throw GosuExceptionUtil.forceThrow( e );
}
catch( InvocationTargetException e )
{
throw GosuExceptionUtil.forceThrow( e.getTargetException() );
}
}
public void setValue( Object ctx, Object value )
{
if( !isWritable( getOwnersType()) )
{
throw new EvaluationException( "Can't set value on read-only property: " + getDisplayName() );
}
String methodName = NameResolver.getSetterNameForDPS( _dps );
try
{
Object[] args;
if( AbstractElementTransformer.requiresImplicitEnhancementArg( _dps.getSetterDfs() ) )
{
IGosuEnhancementInternal enhancement = (IGosuEnhancementInternal)_dps.getSetterDfs().getGosuClass();
List<Object> argList = new ArrayList<Object>();
argList.add( ctx );
if(enhancement.isParameterizedType() )
{
IType[] parameters = enhancement.getTypeParameters();
for( IType parameter : parameters )
{
argList.add( parameter );
}
}
else
{
IGenericTypeVariable[] typeVariables = enhancement.getGenericTypeVariables();
for( IGenericTypeVariable typeVariable : typeVariables )
{
argList.add( typeVariable.getBoundingType() );
}
}
argList.add( value );
args = argList.toArray( new Object[argList.size()] );
}
else
{
args = new Object[]{value};
}
IRMethod setterMethod = IRPropertyFactory.createIRProperty(GosuPropertyInfo.this).getSetterMethod();
List<IRType> allParameterTypes = setterMethod.getAllParameterTypes();
Class[] paramClasses = new Class[allParameterTypes.size()];
for (int i = 0; i < allParameterTypes.size(); i++) {
paramClasses[i] = allParameterTypes.get(i).getJavaClass();
}
Method method = GosuMethodInfo.getMethod( getOwnersType().getBackingClass(), methodName, paramClasses );
method.invoke( ctx, args );
}
catch( InvocationTargetException e )
{
throw GosuExceptionUtil.forceThrow( e.getTargetException() );
}
catch( IllegalAccessException e )
{
throw GosuExceptionUtil.forceThrow( e );
}
}
}
@Override
public IMethodInfo getReadMethodInfo() {
ReducedDynamicFunctionSymbol getterDfs = _dps.getGetterDfs();
if(getterDfs != null) {
return (IMethodInfo) getterDfs.getMethodOrConstructorInfo();
} else if(isReadable() && _dps.getParent() instanceof IMethodBackedPropertyInfo) {
return ((IMethodBackedPropertyInfo)_dps.getParent()).getReadMethodInfo();
}
return null;
}
@Override
public IMethodInfo getWriteMethodInfo() {
ReducedDynamicFunctionSymbol setterDfs = _dps.getSetterDfs();
if(setterDfs != null) {
return (IMethodInfo) setterDfs.getMethodOrConstructorInfo();
}
// else if(isWritable() && _dps.getParent() instanceof IMethodBackedPropertyInfo) {
// return ((IMethodBackedPropertyInfo)_dps.getParent()).getWriteMethodInfo();
// }
return null;
}
public String toString() {
return getName();
}
}