package org.qi4j.runtime.entity;
import com.sun.corba.se.impl.orbutil.ObjectWriter;
import org.qi4j.api.association.AssociationDescriptor;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.association.Association;
import org.qi4j.api.association.AssociationStateHolder;
import org.qi4j.api.association.ManyAssociation;
import org.qi4j.api.property.Property;
import org.qi4j.api.property.PropertyDescriptor;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.util.Classes;
import org.qi4j.functional.Function;
import org.qi4j.functional.Function2;
import org.qi4j.functional.Iterables;
import org.qi4j.runtime.association.AssociationInstance;
import org.qi4j.runtime.association.AssociationModel;
import org.qi4j.runtime.association.ManyAssociationInstance;
import org.qi4j.runtime.association.ManyAssociationModel;
import org.qi4j.runtime.composite.ConstraintsCheck;
import org.qi4j.runtime.property.PropertyModel;
import org.qi4j.runtime.unitofwork.BuilderEntityState;
import org.qi4j.spi.entity.EntityState;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
/**
* TODO
*/
public final class EntityStateInstance
implements AssociationStateHolder
{
protected Map<AccessibleObject, Object> state;
private final EntityStateModel stateModel;
private EntityState entityState;
protected Function2<EntityReference, Type, Object> entityFunction;
public EntityStateInstance(EntityStateModel stateModel, final UnitOfWork uow, EntityState entityState
)
{
this.stateModel = stateModel;
this.entityState = entityState;
entityFunction = new Function2<EntityReference, Type, Object>()
{
@Override
public Object map( EntityReference entityReference, Type type )
{
return uow.get( Classes.RAW_CLASS.map( type ), entityReference.identity() );
}
};
}
public <T> Property<T> propertyFor( AccessibleObject accessor )
throws IllegalArgumentException
{
Map<AccessibleObject, Object> state = state();
Property<T> property = (Property<T>) state.get( accessor );
if( property == null )
{
PropertyModel entityPropertyModel = stateModel.getProperty( accessor );
if (entityPropertyModel == null)
throw new IllegalArgumentException("No such property:"+accessor);
property = new EntityPropertyInstance<T>( entityState instanceof BuilderEntityState ? entityPropertyModel.getBuilderInfo() : entityPropertyModel, entityState);
state.put( accessor, property );
}
return property;
}
@Override
public Iterable<Property<?>> properties()
{
return Iterables.map( new Function<PropertyDescriptor, Property<?>>()
{
@Override
public Property<?> map( PropertyDescriptor propertyDescriptor )
{
return propertyFor( propertyDescriptor.accessor() );
}
}, stateModel.properties());
}
public <T> Association<T> associationFor( AccessibleObject accessor )
throws IllegalArgumentException
{
Map<AccessibleObject, Object> state = state();
Association<T> association = (Association<T>) state.get( accessor );
if( association == null )
{
final AssociationModel associationModel = stateModel.getAssociation( accessor );
if (associationModel == null)
throw new IllegalArgumentException("No such association:"+accessor);
association = new AssociationInstance<T>( entityState instanceof BuilderEntityState ? associationModel.getBuilderInfo() : associationModel, entityFunction, new Property<EntityReference>()
{
@Override
public EntityReference get()
{
return entityState.getAssociation( associationModel.qualifiedName() );
}
@Override
public void set( EntityReference newValue ) throws IllegalArgumentException, IllegalStateException
{
entityState.setAssociation( associationModel.qualifiedName(), newValue );
}
});
state.put( accessor, association );
}
return association;
}
@Override
public Iterable<Association<?>> associations()
{
return Iterables.map( new Function<AssociationDescriptor, Association<?>>()
{
@Override
public Association<?> map( AssociationDescriptor associationDescriptor )
{
return associationFor( associationDescriptor.accessor() );
}
}, stateModel.associations() );
}
public <T> ManyAssociation<T> manyAssociationFor( AccessibleObject accessor )
{
Map<AccessibleObject, Object> state = state();
ManyAssociation<T> manyAssociation = (ManyAssociation<T>) state.get( accessor );
if( manyAssociation == null )
{
final ManyAssociationModel associationModel = stateModel.getManyAssociation( accessor );
if (associationModel == null)
throw new IllegalArgumentException("No such many-association:"+accessor);
manyAssociation = new ManyAssociationInstance<T>( entityState instanceof BuilderEntityState ? associationModel.getBuilderInfo() : associationModel, entityFunction, entityState.getManyAssociation( associationModel.qualifiedName() ));
state.put( accessor, manyAssociation );
}
return manyAssociation;
}
@Override
public Iterable<ManyAssociation<?>> manyAssociations()
{
return Iterables.map( new Function<AssociationDescriptor, ManyAssociation<?>>()
{
@Override
public ManyAssociation<?> map( AssociationDescriptor associationDescriptor )
{
return manyAssociationFor( associationDescriptor.accessor() );
}
}, stateModel.manyAssociations() );
}
public void checkConstraints()
{
for( PropertyDescriptor propertyDescriptor : stateModel.properties() )
{
((ConstraintsCheck)propertyDescriptor).checkConstraints( this.<Object>propertyFor( propertyDescriptor.accessor() ).get() );
}
for( AssociationDescriptor associationDescriptor : stateModel.associations() )
{
((ConstraintsCheck)associationDescriptor).checkConstraints( this.<Object>associationFor( associationDescriptor.accessor() ).get() );
}
// TODO Should ManyAssociations be checked too?
}
private Map<AccessibleObject, Object> state()
{
if (state == null)
{
state = new HashMap<AccessibleObject, Object>();
}
return state;
}
}