package com.buschmais.xo.impl;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import com.buschmais.xo.api.XOException;
import com.buschmais.xo.spi.datastore.DatastorePropertyManager;
import com.buschmais.xo.spi.datastore.DatastoreRelationManager;
import com.buschmais.xo.spi.datastore.DatastoreRelationMetadata;
import com.buschmais.xo.spi.metadata.method.*;
import com.buschmais.xo.spi.metadata.type.RelationTypeMetadata;
public class EntityPropertyManager<Entity, Relation, PropertyMetadata> extends AbstractPropertyManager<Entity> {
private final SessionContext<?, Entity, ?, ?, ?, Relation, ?, ?, PropertyMetadata> sessionContext;
/**
* Constructor.
*
* @param sessionContext
* The {@link SessionContext}.
*/
public EntityPropertyManager(SessionContext<?, Entity, ?, ?, ?, Relation, ?, ?, PropertyMetadata> sessionContext) {
this.sessionContext = sessionContext;
}
@Override
protected DatastorePropertyManager<Entity, ?> getDatastorePropertyManager() {
return sessionContext.getDatastoreSession().getDatastoreEntityManager();
}
@Override
protected AbstractInstanceManager<?, Entity> getInstanceManager() {
return sessionContext.getEntityInstanceManager();
}
public void createEntityReference(Entity sourceEntity, AbstractRelationPropertyMethodMetadata<?> metadata, Object target) {
AbstractInstanceManager<?, Entity> instanceManager = sessionContext.getEntityInstanceManager();
Entity targetEntity = target != null ? instanceManager.getDatastoreType(target) : null;
createRelation(sourceEntity, metadata, targetEntity, null, Collections.emptyMap());
}
public <T> T createRelationReference(Entity sourceEntity, AbstractRelationPropertyMethodMetadata<?> fromProperty, Object target,
AbstractRelationPropertyMethodMetadata<?> toProperty, Map<PrimitivePropertyMethodMetadata<PropertyMetadata>, Object> example) {
AbstractInstanceManager<?, Entity> entityInstanceManager = sessionContext.getEntityInstanceManager();
if (target != null) {
Entity targetEntity = entityInstanceManager.getDatastoreType(target);
Relation relation = createRelation(sourceEntity, fromProperty, targetEntity, toProperty, example);
return sessionContext.getRelationInstanceManager().createInstance(relation);
}
return null;
}
public Object getEntityReference(Entity entity, EntityReferencePropertyMethodMetadata metadata) {
DatastoreRelationManager<Entity, ?, Relation, ?, ?, ?> relationManager = sessionContext.getDatastoreSession().getDatastoreRelationManager();
if (relationManager.hasSingleRelation(entity, metadata.getRelationshipMetadata(), metadata.getDirection())) {
Relation singleRelation = (Relation) relationManager.getSingleRelation(entity, metadata.getRelationshipMetadata(), metadata.getDirection());
Entity target = getReferencedEntity(singleRelation, metadata.getDirection());
return sessionContext.getEntityInstanceManager().readInstance(target);
}
return null;
}
public Iterator<Entity> getEntityCollection(Entity entity, final EntityCollectionPropertyMethodMetadata<?> metadata) {
Iterable<Relation> relations = sessionContext.getDatastoreSession().getDatastoreRelationManager().getRelations(entity,
metadata.getRelationshipMetadata(), metadata.getDirection());
final Iterator<Relation> iterator = relations.iterator();
return new Iterator<Entity>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Entity next() {
Relation next = iterator.next();
return getReferencedEntity(next, metadata.getDirection());
}
@Override
public void remove() {
}
};
}
public Object getRelationReference(Entity entity, RelationReferencePropertyMethodMetadata<?> metadata) {
DatastoreRelationManager<Entity, ?, Relation, ? extends DatastoreRelationMetadata<?>, ?, ?> relationManager = sessionContext.getDatastoreSession()
.getDatastoreRelationManager();
if (relationManager.hasSingleRelation(entity, metadata.getRelationshipMetadata(), metadata.getDirection())) {
Relation singleRelation = (Relation) relationManager.getSingleRelation(entity, metadata.getRelationshipMetadata(), metadata.getDirection());
return sessionContext.getRelationInstanceManager().readInstance(singleRelation);
}
return null;
}
public Iterator<Relation> getRelationCollection(Entity entity, RelationCollectionPropertyMethodMetadata<?> metadata) {
Iterable<Relation> relations = sessionContext.getDatastoreSession().getDatastoreRelationManager().getRelations(entity,
metadata.getRelationshipMetadata(), metadata.getDirection());
return relations.iterator();
}
public void removeEntityReferences(Entity entity, EntityCollectionPropertyMethodMetadata metadata) {
Iterable<Relation> relations = sessionContext.getDatastoreSession().getDatastoreRelationManager().getRelations(entity,
metadata.getRelationshipMetadata(), metadata.getDirection());
for (Relation relation : relations) {
removeRelation(entity, relation, metadata);
}
}
public boolean removeEntityReference(Entity entity, EntityCollectionPropertyMethodMetadata<?> metadata, Object target) {
Iterable<Relation> relations = sessionContext.getDatastoreSession().getDatastoreRelationManager().getRelations(entity,
metadata.getRelationshipMetadata(), metadata.getDirection());
Entity targetEntity = sessionContext.getEntityInstanceManager().getDatastoreType(target);
for (Relation relation : relations) {
Entity referencedEntity = getReferencedEntity(relation, metadata.getDirection());
if (referencedEntity.equals(targetEntity)) {
removeRelation(entity, relation, metadata);
return true;
}
}
return false;
}
private void removeRelation(Entity source, Relation relation, AbstractRelationPropertyMethodMetadata<?> metadata) {
AbstractInstanceManager<?, Entity> entityInstanceManager = sessionContext.getEntityInstanceManager();
entityInstanceManager.updateInstance(source);
entityInstanceManager.updateInstance(getReferencedEntity(relation, metadata.getDirection()));
sessionContext.getDatastoreSession().getDatastoreRelationManager().deleteRelation(relation);
AbstractInstanceManager<?, Relation> relationInstanceManager = sessionContext.getRelationInstanceManager();
if (metadata.getRelationshipMetadata().getAnnotatedType() != null) {
Object instance = relationInstanceManager.readInstance(relation);
relationInstanceManager.removeInstance(instance);
relationInstanceManager.closeInstance(instance);
}
}
private Entity getReferencedEntity(Relation relation, RelationTypeMetadata.Direction direction) {
DatastoreRelationManager<Entity, ?, Relation, ? extends DatastoreRelationMetadata<?>, ?, ?> relationManager = sessionContext.getDatastoreSession()
.getDatastoreRelationManager();
switch (direction) {
case FROM:
return relationManager.getTo(relation);
case TO:
return relationManager.getFrom(relation);
default:
throw new XOException("Unsupported direction: " + direction);
}
}
private Relation createRelation(Entity sourceEntity, AbstractRelationPropertyMethodMetadata<?> fromProperty, Entity targetEntity,
AbstractRelationPropertyMethodMetadata<?> toProperty, Map<PrimitivePropertyMethodMetadata<PropertyMetadata>, Object> example) {
Relation relation;
if (fromProperty instanceof EntityReferencePropertyMethodMetadata || fromProperty instanceof RelationReferencePropertyMethodMetadata) {
relation = createSingleReference(sourceEntity, fromProperty, targetEntity, example);
} else if (toProperty instanceof EntityReferencePropertyMethodMetadata || toProperty instanceof RelationReferencePropertyMethodMetadata) {
relation = createSingleReference(targetEntity, toProperty, sourceEntity, example);
} else if (fromProperty instanceof EntityCollectionPropertyMethodMetadata || fromProperty instanceof RelationCollectionPropertyMethodMetadata) {
relation = createReference(sourceEntity, fromProperty.getRelationshipMetadata(), fromProperty.getDirection(), targetEntity, example);
} else {
throw new XOException("Unsupported relation type " + fromProperty.getClass().getName());
}
return relation;
}
private Relation createSingleReference(Entity sourceEntity, AbstractRelationPropertyMethodMetadata<?> metadata, Entity targetEntity,
Map<PrimitivePropertyMethodMetadata<PropertyMetadata>, Object> example) {
DatastoreRelationManager<Entity, ?, Relation, ? extends DatastoreRelationMetadata<?>, ?, PropertyMetadata> relationManager = sessionContext
.getDatastoreSession().getDatastoreRelationManager();
AbstractInstanceManager<?, Entity> entityInstanceManager = sessionContext.getEntityInstanceManager();
if (relationManager.hasSingleRelation(sourceEntity, metadata.getRelationshipMetadata(), metadata.getDirection())) {
Relation relation = (Relation) relationManager.getSingleRelation(sourceEntity, metadata.getRelationshipMetadata(), metadata.getDirection());
Entity referencedEntity = getReferencedEntity(relation, metadata.getDirection());
entityInstanceManager.updateInstance(referencedEntity);
removeRelation(sourceEntity, relation, metadata);
}
entityInstanceManager.updateInstance(sourceEntity);
if (targetEntity != null) {
Relation relation = (Relation) relationManager.createRelation(sourceEntity, metadata.getRelationshipMetadata(), metadata.getDirection(),
targetEntity, example);
entityInstanceManager.updateInstance(targetEntity);
return relation;
}
return null;
}
private Relation createReference(Entity sourceEntity, RelationTypeMetadata metadata, RelationTypeMetadata.Direction direction, Entity targetEntity,
Map<PrimitivePropertyMethodMetadata<PropertyMetadata>, Object> example) {
DatastoreRelationManager<Entity, ?, Relation, ?, ?, PropertyMetadata> datastoreRelationManager = sessionContext.getDatastoreSession()
.getDatastoreRelationManager();
Relation relation = (Relation) datastoreRelationManager.createRelation(sourceEntity, metadata, direction, targetEntity, example);
AbstractInstanceManager<?, Entity> entityInstanceManager = sessionContext.getEntityInstanceManager();
entityInstanceManager.updateInstance(sourceEntity);
entityInstanceManager.updateInstance(targetEntity);
return relation;
}
}