package com.buschmais.xo.impl.metadata; import java.util.*; import com.buschmais.xo.api.XOException; import com.buschmais.xo.api.bootstrap.XOUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.buschmais.xo.spi.datastore.DatastoreEntityMetadata; import com.buschmais.xo.spi.datastore.TypeMetadataSet; import com.buschmais.xo.spi.metadata.type.EntityTypeMetadata; import com.buschmais.xo.spi.metadata.type.TypeMetadata; /** * Allows resolving types from entity discriminators as provided by the * datastores. * * @param <Discriminator> * The discriminator type of the datastore (e.g. Neo4j labels or * strings for JSON stores). */ public class EntityTypeMetadataResolver<EntityMetadata extends DatastoreEntityMetadata<Discriminator>, Discriminator> { private static final Logger LOGGER = LoggerFactory.getLogger(EntityTypeMetadataResolver.class); private final Map<EntityTypeMetadata<EntityMetadata>, Set<Discriminator>> aggregatedDiscriminators = new HashMap<>(); private final Map<Discriminator, Set<EntityTypeMetadata<EntityMetadata>>> typeMetadataByDiscriminator = new HashMap<>(); /** * Constructor. * * @param metadataByType * A map of all types with their metadata. */ public EntityTypeMetadataResolver(Map<Class<?>, TypeMetadata> metadataByType, XOUnit.MappingConfiguration mappingConfiguration) { LOGGER.debug("Type metadata = '{}'", metadataByType); Map<Set<Discriminator>, Set<EntityTypeMetadata<EntityMetadata>>> entityMetadataByDiscriminators = new HashMap<>(); for (TypeMetadata typeMetadata : metadataByType.values()) { if (typeMetadata instanceof EntityTypeMetadata) { EntityTypeMetadata<EntityMetadata> entityTypeMetadata = (EntityTypeMetadata<EntityMetadata>) typeMetadata; Set<Discriminator> discriminators = getAggregatedDiscriminators(entityTypeMetadata); Set<EntityTypeMetadata<EntityMetadata>> typeMetadataOfDiscriminators = entityMetadataByDiscriminators.get(discriminators); if (typeMetadataOfDiscriminators == null) { typeMetadataOfDiscriminators = new HashSet<>(); entityMetadataByDiscriminators.put(discriminators, typeMetadataOfDiscriminators); } typeMetadataOfDiscriminators.add(entityTypeMetadata); LOGGER.debug("Aggregated discriminators of '{}' = '{}'", typeMetadata, discriminators); } } for (TypeMetadata typeMetadata : metadataByType.values()) { if (typeMetadata instanceof EntityTypeMetadata) { Set<Discriminator> discriminators = aggregatedDiscriminators.get(typeMetadata); for (Discriminator discriminator : discriminators) { Set<EntityTypeMetadata<EntityMetadata>> entityTypeMetadataOfDiscriminator = typeMetadataByDiscriminator.get(discriminator); if (entityTypeMetadataOfDiscriminator == null) { entityTypeMetadataOfDiscriminator = new HashSet<>(); typeMetadataByDiscriminator.put(discriminator, entityTypeMetadataOfDiscriminator); } entityTypeMetadataOfDiscriminator.add((EntityTypeMetadata<EntityMetadata>) typeMetadata); } } } LOGGER.debug("Type metadata by discriminators: '{}'", typeMetadataByDiscriminator); for (Map.Entry<Set<Discriminator>, Set<EntityTypeMetadata<EntityMetadata>>> entry : entityMetadataByDiscriminators.entrySet()) { if (entry.getValue().size() > 1) { String message = String.format("%s use the same set of discriminators %s.", entry.getValue(), entry.getKey()); if (mappingConfiguration.isStrictValidation()) { throw new XOException(message); } else { LOGGER.warn(message); } } } } /** * Determine the set of discriminators for one type, i.e. the discriminator * of the type itself and of all it's super types. * * @param typeMetadata * The type. * @return The set of discriminators. */ private Set<Discriminator> getAggregatedDiscriminators(EntityTypeMetadata<EntityMetadata> typeMetadata) { Set<Discriminator> discriminators = aggregatedDiscriminators.get(typeMetadata); if (discriminators == null) { discriminators = new HashSet<>(); Discriminator discriminator = typeMetadata.getDatastoreMetadata().getDiscriminator(); if (discriminator != null) { discriminators.add(discriminator); } for (TypeMetadata superTypeMetadata : typeMetadata.getSuperTypes()) { if (superTypeMetadata instanceof EntityTypeMetadata) { discriminators.addAll(getAggregatedDiscriminators((EntityTypeMetadata<EntityMetadata>) superTypeMetadata)); } } aggregatedDiscriminators.put(typeMetadata, discriminators); } return discriminators; } /** * Return a {@link com.buschmais.xo.spi.datastore.TypeMetadataSet} * containing all types matching to the given entity discriminators. * * @param discriminators * The discriminators. * @return The {@link com.buschmais.xo.spi.datastore.TypeMetadataSet}. */ public TypeMetadataSet<EntityTypeMetadata<EntityMetadata>> getTypes(Set<Discriminator> discriminators) { // Get all types matching the discriminators TypeMetadataSet<EntityTypeMetadata<EntityMetadata>> allEntityTypeMetadatas = new TypeMetadataSet<>(); for (Discriminator discriminator : discriminators) { Set<EntityTypeMetadata<EntityMetadata>> entityTypeMetadataOfDiscriminator = typeMetadataByDiscriminator.get(discriminator); if (entityTypeMetadataOfDiscriminator != null) { for (EntityTypeMetadata<EntityMetadata> entityTypeMetadata : entityTypeMetadataOfDiscriminator) { Set<Discriminator> entityDiscriminators = aggregatedDiscriminators.get(entityTypeMetadata); if (discriminators.size() >= entityDiscriminators.size() && discriminators.containsAll(entityDiscriminators)) { allEntityTypeMetadatas.add(entityTypeMetadata); } } } } return allEntityTypeMetadatas; } public Set<Discriminator> getDiscriminators(EntityTypeMetadata<EntityMetadata> entityTypeMetadata) { Set<Discriminator> discriminators = aggregatedDiscriminators.get(entityTypeMetadata); return discriminators != null ? discriminators : Collections.<Discriminator> emptySet(); } }