package org.javers.core.metamodel.object;
import org.javers.common.exception.JaversException;
import org.javers.common.exception.JaversExceptionCode;
import org.javers.common.validation.Validate;
import org.javers.core.graph.ObjectAccessHook;
import org.javers.core.metamodel.type.*;
import org.javers.core.snapshot.ObjectHasher;
import org.javers.guava.MultimapType;
import org.javers.guava.MultisetType;
import org.javers.repository.jql.GlobalIdDTO;
import org.javers.repository.jql.InstanceIdDTO;
import org.javers.repository.jql.UnboundedValueObjectIdDTO;
import org.javers.repository.jql.ValueObjectIdDTO;
import org.picocontainer.PicoContainer;
/**
* @author bartosz walacik
*/
public class GlobalIdFactory {
private final TypeMapper typeMapper;
private ObjectAccessHook objectAccessHook;
private final GlobalIdPathParser pathParser;
private ObjectHasher objectHasher;
private final PicoContainer picoContainer;
public GlobalIdFactory(TypeMapper typeMapper, ObjectAccessHook objectAccessHook, PicoContainer container) {
this.typeMapper = typeMapper;
this.objectAccessHook = objectAccessHook;
this.pathParser = new GlobalIdPathParser(typeMapper);
this.picoContainer = container;
}
public GlobalId createId(Object targetCdo) {
return createId(targetCdo, null);
}
/**
* @param ownerContext for bounded ValueObjects, optional
*/
public GlobalId createId(Object targetCdo, OwnerContext ownerContext) {
Validate.argumentsAreNotNull(targetCdo);
targetCdo = objectAccessHook.access(targetCdo);
ManagedType targetManagedType = typeMapper.getJaversManagedType(targetCdo.getClass());
if (targetManagedType instanceof EntityType) {
return InstanceId.createFromInstance(targetCdo, (EntityType) targetManagedType);
}
if (targetManagedType instanceof ValueObjectType && !hasOwner(ownerContext)) {
return new UnboundedValueObjectId(targetManagedType.getName());
}
if (targetManagedType instanceof ValueObjectType && hasOwner(ownerContext)) {
String pathFromRoot = createPathFromRoot(ownerContext.getOwnerId(), ownerContext.getPath());
if (ownerContext.requiresObjectHasher()) {
pathFromRoot += "/" + getObjectHasher().hash(targetCdo);
}
return new ValueObjectId(targetManagedType.getName(), getRootOwnerId(ownerContext), pathFromRoot);
}
throw new JaversException(JaversExceptionCode.NOT_IMPLEMENTED);
}
private GlobalId getRootOwnerId(OwnerContext ownerContext) {
GlobalId rootOwnerId;
if (ownerContext.getOwnerId() instanceof ValueObjectId){
rootOwnerId = ((ValueObjectId)ownerContext.getOwnerId()).getOwnerId();
} else{
rootOwnerId = ownerContext.getOwnerId();
}
return rootOwnerId;
}
private String createPathFromRoot(GlobalId parentId, String fragment) {
if (parentId instanceof ValueObjectId){
return ((ValueObjectId)parentId).getFragment()+"/"+fragment;
} else{
return fragment;
}
}
public UnboundedValueObjectId createUnboundedValueObjectId(Class valueObjectClass){
ValueObjectType valueObject = typeMapper.getJaversManagedType(valueObjectClass, ValueObjectType.class);
return new UnboundedValueObjectId(valueObject.getName());
}
public InstanceId createInstanceId(Object instance){
Validate.argumentIsNotNull(instance);
EntityType entityType = typeMapper.getJaversManagedType(instance.getClass(), EntityType.class);
return createInstanceId(entityType.getIdOf(instance), entityType);
}
public InstanceId createInstanceId(Object localId, EntityType entity){
return new InstanceId(entity.getName(), localId);
}
@Deprecated
public ValueObjectId createValueObjectIdFromPath(GlobalId owner, String fragment){
ManagedType ownerType = typeMapper.getJaversManagedType(owner);
ValueObjectType valueObjectType = pathParser.parseChildValueObject(ownerType,fragment);
return new ValueObjectId(valueObjectType.getName(), owner, fragment);
}
public ValueObjectId createValueObjectId(String voTypeName, GlobalId owner, String fragment){
Validate.argumentsAreNotNull(voTypeName, owner, fragment);
ValueObjectType valueObjectType = typeMapper.getJaversManagedType(voTypeName, ValueObjectType.class);
return new ValueObjectId(valueObjectType.getName(), owner, fragment);
}
public void touchValueObjectFromPath(ManagedType ownerType, String fragment){
pathParser.parseChildValueObject(ownerType, fragment);
}
public InstanceId createInstanceId(Object localId, Class entityClass){
EntityType entity = typeMapper.getJaversManagedType(entityClass, EntityType.class);
return createInstanceId(localId, entity);
}
public GlobalId createFromDto(GlobalIdDTO globalIdDTO){
if (globalIdDTO instanceof InstanceIdDTO){
InstanceIdDTO idDTO = (InstanceIdDTO) globalIdDTO;
return createInstanceId(idDTO.getCdoId(), idDTO.getEntity());
}
if (globalIdDTO instanceof UnboundedValueObjectIdDTO){
UnboundedValueObjectIdDTO idDTO = (UnboundedValueObjectIdDTO) globalIdDTO;
return createUnboundedValueObjectId(idDTO.getVoClass());
}
if (globalIdDTO instanceof ValueObjectIdDTO){
ValueObjectIdDTO idDTO = (ValueObjectIdDTO) globalIdDTO;
GlobalId ownerId = createFromDto(idDTO.getOwnerIdDTO());
return createValueObjectIdFromPath(ownerId, idDTO.getPath());
}
throw new RuntimeException("type " + globalIdDTO.getClass() + " is not implemented");
}
/**
* If item is Primitive or Value - returns it,
* if item is Entity or ValueObject - returns its globalId,
* if item is already instance of GlobalId - returns it.
*/
public Object dehydrate(Object item, JaversType targetType, OwnerContext context){
if (item == null) {
return null;
}
if (!(item instanceof GlobalId) && targetType instanceof ManagedType) {
return createId(item, context);
} else {
return item;
}
}
//pico can't resolve cycles
private ObjectHasher getObjectHasher(){
if (objectHasher != null){
return objectHasher;
}
synchronized (this) {
objectHasher = picoContainer.getComponent(ObjectHasher.class);
return objectHasher;
}
}
private boolean hasOwner(OwnerContext context) {
return context != null && context.getOwnerId() != null;
}
}