Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.molgenis.data.EntityManagerImpl Maven / Gradle / Ivy
package org.molgenis.data;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.StreamSupport.stream;
import static org.molgenis.data.EntityManager.CreationMode.NO_POPULATE;
import static org.molgenis.data.EntityManager.CreationMode.POPULATE;
import static org.molgenis.data.util.EntityTypeUtils.isMultipleReferenceType;
import static org.molgenis.data.util.EntityTypeUtils.isSingleReferenceType;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterators;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Streams;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.populate.EntityPopulator;
import org.molgenis.data.support.DynamicEntity;
import org.molgenis.data.support.EntityWithComputedAttributes;
import org.molgenis.data.support.PartialEntity;
import org.molgenis.data.util.EntityTypeUtils;
import org.springframework.stereotype.Component;
/**
* Entity manager responsible for creating entities, entity references and resolving references of
* reference attributes.
*/
@Component
public class EntityManagerImpl implements EntityManager {
private static final int BATCH_SIZE = 100;
private final DataService dataService;
private final EntityFactoryRegistry entityFactoryRegistry;
private final EntityPopulator entityPopulator;
private final EntityReferenceCreator entityReferenceCreator;
public EntityManagerImpl(
DataService dataService,
EntityFactoryRegistry entityFactoryRegistry,
EntityPopulator entityPopulator,
EntityReferenceCreator entityReferenceCreator) {
this.dataService = requireNonNull(dataService);
this.entityFactoryRegistry = requireNonNull(entityFactoryRegistry);
this.entityPopulator = requireNonNull(entityPopulator);
this.entityReferenceCreator = requireNonNull(entityReferenceCreator);
}
@Override
public Entity create(EntityType entityType, CreationMode creationMode) {
return create(entityType, null, creationMode);
}
@Override
public Entity createFetch(EntityType entityType, Fetch fetch) {
return create(entityType, fetch, NO_POPULATE);
}
private Entity create(EntityType entityType, Fetch fetch, CreationMode creationMode) {
Entity entity = new DynamicEntity(entityType);
if (fetch != null) {
// create partial entity that loads attribute values not contained in the fetch on demand.
entity = new PartialEntity(entity, fetch, this);
}
if (entityType.hasAttributeWithExpression()) {
// create entity that computed values based on expressions defined in meta data
entity = new EntityWithComputedAttributes(entity);
}
if (creationMode == POPULATE) {
entityPopulator.populate(entity);
}
EntityFactory extends Entity, ?> entityFactory =
entityFactoryRegistry.getEntityFactory(entityType);
if (entityFactory != null) {
// create static entity (e.g. Tag, Language, Package) that wraps the constructed dynamic or
// partial entity.
return entityFactory.create(entity);
}
return entity;
}
@Override
public Entity getReference(EntityType entityType, Object id) {
return entityReferenceCreator.getReference(entityType, id);
}
@Override
public Iterable getReferences(EntityType entityType, Iterable> ids) {
return entityReferenceCreator.getReferences(entityType, ids);
}
@Override
public Entity resolveReferences(EntityType entityType, Entity entity, Fetch fetch) {
// no fetch exists that described what to resolve
if (fetch == null) {
return entity;
}
List resolvableAttrs = getResolvableAttrs(entityType, fetch);
// entity has no references, nothing to resolve
if (resolvableAttrs.isEmpty()) {
return entity;
}
return resolveReferences(resolvableAttrs, singletonList(entity), fetch).iterator().next();
}
@Override
public Stream resolveReferences(
EntityType entityType, Stream entities, Fetch fetch) {
// resolve lazy entity collections without references
if (entities instanceof EntityStream && ((EntityStream) entities).isLazy()) {
// TODO remove cast after updating DataService/Repository interfaces to return EntityStream
return dataService.findAll(entityType.getId(), entities.map(Entity::getIdValue), fetch);
}
// no fetch exists that described what to resolve
if (fetch == null) {
return entities;
}
List resolvableAttrs = getResolvableAttrs(entityType, fetch);
// entity has no references, nothing to resolve
if (resolvableAttrs.isEmpty()) {
return entities;
}
Iterable> iterable = () -> Iterators.partition(entities.iterator(), BATCH_SIZE);
return Streams.stream(iterable)
.flatMap(
batch -> {
List batchWithReferences = resolveReferences(resolvableAttrs, batch, fetch);
return batchWithReferences.stream();
});
}
private List resolveReferences(
List resolvableAttrs, List entities, Fetch fetch) {
// entity name --> entity ids
SetMultimap lazyRefEntityIdsMap =
HashMultimap.create(resolvableAttrs.size(), 16);
// entity name --> attributes referring to this entity
SetMultimap refEntityAttrsMap =
HashMultimap.create(resolvableAttrs.size(), 2);
// fill maps
for (Attribute attr : resolvableAttrs) {
String refEntityName = attr.getRefEntity().getId();
if (isSingleReferenceType(attr)) {
for (Entity entity : entities) {
Entity lazyRefEntity = entity.getEntity(attr.getName());
if (lazyRefEntity != null) {
lazyRefEntityIdsMap.put(refEntityName, lazyRefEntity.getIdValue());
}
}
} else if (isMultipleReferenceType(attr)) {
for (Entity entity : entities) {
Iterable lazyRefEntities = entity.getEntities(attr.getName());
for (Entity lazyRefEntity : lazyRefEntities) {
lazyRefEntityIdsMap.put(refEntityName, lazyRefEntity.getIdValue());
}
}
}
refEntityAttrsMap.put(refEntityName, attr);
}
// batch retrieve referred entities and replace entity references with actual entities
for (Entry> entry : lazyRefEntityIdsMap.asMap().entrySet()) {
String refEntityName = entry.getKey();
// create a fetch for the referenced entity which is a union of the fetches defined by
// attributes
// referencing this entity
Set attrs = refEntityAttrsMap.get(refEntityName);
Fetch subFetch = createSubFetch(fetch, attrs);
// retrieve referenced entities
Stream refEntities =
dataService.findAll(refEntityName, entry.getValue().stream(), subFetch);
Map refEntitiesIdMap =
refEntities.collect(Collectors.toMap(Entity::getIdValue, Function.identity()));
for (Attribute attr : attrs) {
if (isSingleReferenceType(attr)) {
String attrName = attr.getName();
for (Entity entity : entities) {
Entity lazyRefEntity = entity.getEntity(attrName);
if (lazyRefEntity != null) {
// replace lazy entity with real entity
Object refEntityId = lazyRefEntity.getIdValue();
Entity refEntity = refEntitiesIdMap.get(refEntityId);
entity.set(attrName, refEntity);
}
}
} else if (isMultipleReferenceType(attr)) {
String attrName = attr.getName();
for (Entity entity : entities) {
// replace lazy entities with real entities
Iterable lazyRefEntities = entity.getEntities(attrName);
List mrefEntities =
stream(lazyRefEntities.spliterator(), true)
.map(
lazyRefEntity -> {
// replace lazy entity with real entity
Object refEntityId = lazyRefEntity.getIdValue();
return refEntitiesIdMap.get(refEntityId);
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
entity.set(attrName, mrefEntities);
}
}
}
}
return entities;
}
private static Fetch createSubFetch(Fetch fetch, Iterable attrs) {
Fetch subFetch = null;
for (Attribute attr : attrs) {
Fetch attrSubFetch = fetch.getFetch(attr.getName());
if (attrSubFetch != null) {
// lazy creation
if (subFetch == null) {
subFetch = new Fetch();
}
for (Entry entry : attrSubFetch) {
mergeFetches(subFetch, entry.getKey(), entry.getValue());
}
} else {
// prefer null value (=fetch all attributes) above other values (=filter some attributes)
subFetch = null;
break;
}
}
return subFetch;
}
private static void mergeFetches(Fetch fetch, String field, Fetch subFetch) {
if (subFetch == null) {
// prefer null value above specific value
fetch.field(field, null);
} else if (fetch.hasField(field)) {
Fetch existingSubFetch = fetch.getFetch(field);
if (existingSubFetch != null) {
for (Map.Entry entry : subFetch) {
mergeFetches(existingSubFetch, entry.getKey(), entry.getValue());
}
}
} else {
// first value for this field
fetch.field(field, subFetch);
}
}
/**
* Return all resolvable attributes: non-computed reference attributes defined in fetch
*
* @param entityType entity meta data
* @param fetch entity fetch
* @return resolved attributes
*/
private static List getResolvableAttrs(EntityType entityType, Fetch fetch) {
return Streams.stream(entityType.getAtomicAttributes())
.filter(EntityTypeUtils::isReferenceType)
.filter(attr -> attr.getExpression() == null)
.filter(attr -> fetch.hasField(attr.getName()))
.collect(Collectors.toList());
}
}