package org.hibernate.metamodel.internal;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.MappedSuperclassType;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
class MetadataContext {
private static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( MetadataContext.class );
private final SessionFactoryImplementor sessionFactory;
private Set<MappedSuperclass> knownMappedSuperclasses;
private final boolean ignoreUnsupported;
private final AttributeFactory attributeFactory = new AttributeFactory( this );
private Map<Class<?>, EntityTypeImpl<?>> entityTypes = new HashMap<>();
private Map<String, EntityTypeImpl<?>> entityTypesByEntityName = new HashMap<>();
private Map<PersistentClass, EntityTypeImpl<?>> entityTypesByPersistentClass = new HashMap<>();
private Map<Class<?>, EmbeddableTypeImpl<?>> embeddables = new HashMap<>();
private Map<MappedSuperclass, MappedSuperclassTypeImpl<?>> mappedSuperclassByMappedSuperclassMapping = new HashMap<>();
private List<Object> orderedMappings = new ArrayList<>();
private List<PersistentClass> stackOfPersistentClassesBeingProcessed = new ArrayList<>();
private Map<MappedSuperclassTypeImpl<?>, PersistentClass> mappedSuperClassTypeToPersistentClass = new HashMap<>();
public MetadataContext(
SessionFactoryImplementor sessionFactory,
Set<MappedSuperclass> mappedSuperclasses,
JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting) {
this.sessionFactory = sessionFactory;
this.knownMappedSuperclasses = mappedSuperclasses;
this.ignoreUnsupported = jpaMetaModelPopulationSetting == JpaMetaModelPopulationSetting.IGNORE_UNSUPPORTED;
}
SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
boolean isIgnoreUnsupported() {
return ignoreUnsupported;
}
public Map<Class<?>, EntityTypeImpl<?>> getEntityTypeMap() {
return Collections.unmodifiableMap( entityTypes );
}
public Map<Class<?>, EmbeddableTypeImpl<?>> getEmbeddableTypeMap() {
return Collections.unmodifiableMap( embeddables );
}
public Map<Class<?>, MappedSuperclassType<?>> getMappedSuperclassTypeMap() {
final Map<Class<?>, MappedSuperclassType<?>> mappedSuperClassTypeMap = CollectionHelper.mapOfSize(
mappedSuperclassByMappedSuperclassMapping.size()
);
for ( MappedSuperclassTypeImpl mappedSuperclassType : mappedSuperclassByMappedSuperclassMapping.values() ) {
mappedSuperClassTypeMap.put(
mappedSuperclassType.getJavaType(),
mappedSuperclassType
);
}
return mappedSuperClassTypeMap;
}
void registerEntityType(PersistentClass persistentClass, EntityTypeImpl<?> entityType) {
if ( entityType.getBindableJavaType() != null ) {
entityTypes.put( entityType.getBindableJavaType(), entityType );
}
entityTypesByEntityName.put( persistentClass.getEntityName(), entityType );
entityTypesByPersistentClass.put( persistentClass, entityType );
orderedMappings.add( persistentClass );
}
void registerEmbeddedableType(EmbeddableTypeImpl<?> embeddableType) {
embeddables.put( embeddableType.getJavaType(), embeddableType );
}
void registerMappedSuperclassType(
MappedSuperclass mappedSuperclass,
MappedSuperclassTypeImpl<?> mappedSuperclassType) {
mappedSuperclassByMappedSuperclassMapping.put( mappedSuperclass, mappedSuperclassType );
orderedMappings.add( mappedSuperclass );
mappedSuperClassTypeToPersistentClass.put( mappedSuperclassType, getEntityWorkedOn() );
knownMappedSuperclasses.remove( mappedSuperclass );
}
public EntityTypeImpl<?> locateEntityType(PersistentClass persistentClass) {
return entityTypesByPersistentClass.get( persistentClass );
}
public EntityTypeImpl<?> locateEntityType(Class<?> javaType) {
return entityTypes.get( javaType );
}
public EntityTypeImpl<?> locateEntityType(String entityName) {
return entityTypesByEntityName.get( entityName );
}
public Map<String, EntityTypeImpl<?>> getEntityTypesByEntityName() {
return Collections.unmodifiableMap( entityTypesByEntityName );
}
@SuppressWarnings({"unchecked"})
public void wrapUp() {
final boolean traceEnabled = LOG.isTraceEnabled();
if ( traceEnabled ) {
LOG.trace( "Wrapping up metadata context..." );
}
boolean staticMetamodelScanEnabled = JpaStaticMetaModelPopulationSetting
.determineJpaMetaModelPopulationSetting( sessionFactory.getProperties() ) != JpaStaticMetaModelPopulationSetting.DISABLED;
for ( Object mapping : orderedMappings ) {
if ( PersistentClass.class.isAssignableFrom( mapping.getClass() ) ) {
@SuppressWarnings("unchecked")
final PersistentClass safeMapping = (PersistentClass) mapping;
if ( traceEnabled ) {
LOG.trace( "Starting entity [" + safeMapping.getEntityName() + ']' );
}
try {
final EntityTypeImpl<?> jpa2Mapping = entityTypesByPersistentClass.get( safeMapping );
applyIdMetadata( safeMapping, jpa2Mapping );
applyVersionAttribute( safeMapping, jpa2Mapping );
Iterator<Property> properties = safeMapping.getDeclaredPropertyIterator();
while ( properties.hasNext() ) {
final Property property = properties.next();
if ( property.getValue() == safeMapping.getIdentifierMapper() ) {
continue;
}
if ( safeMapping.isVersioned() && property == safeMapping.getVersion() ) {
continue;
}
final Attribute attribute = attributeFactory.buildAttribute( jpa2Mapping, property );
if ( attribute != null ) {
jpa2Mapping.getBuilder().addAttribute( attribute );
}
}
jpa2Mapping.lock();
if ( staticMetamodelScanEnabled ) {
populateStaticMetamodel( jpa2Mapping );
}
}
finally {
if ( traceEnabled ) {
LOG.trace( "Completed entity [" + safeMapping.getEntityName() + ']' );
}
}
}
else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) {
@SuppressWarnings("unchecked")
final MappedSuperclass safeMapping = (MappedSuperclass) mapping;
if ( traceEnabled ) {
LOG.trace( "Starting mapped superclass [" + safeMapping.getMappedClass().getName() + ']' );
}
try {
final MappedSuperclassTypeImpl<?> jpa2Mapping = mappedSuperclassByMappedSuperclassMapping.get(
safeMapping
);
applyIdMetadata( safeMapping, jpa2Mapping );
applyVersionAttribute( safeMapping, jpa2Mapping );
Iterator<Property> properties = safeMapping.getDeclaredPropertyIterator();
while ( properties.hasNext() ) {
final Property property = properties.next();
if ( safeMapping.isVersioned() && property == safeMapping.getVersion() ) {
continue;
}
final Attribute attribute = attributeFactory.buildAttribute( jpa2Mapping, property );
if ( attribute != null ) {
jpa2Mapping.getBuilder().addAttribute( attribute );
}
}
jpa2Mapping.lock();
if ( staticMetamodelScanEnabled ) {
populateStaticMetamodel( jpa2Mapping );
}
}
finally {
if ( traceEnabled ) {
LOG.trace( "Completed mapped superclass [" + safeMapping.getMappedClass().getName() + ']' );
}
}
}
else {
throw new AssertionFailure( "Unexpected mapping type: " + mapping.getClass() );
}
}
if ( staticMetamodelScanEnabled ) {
for ( EmbeddableTypeImpl embeddable : embeddables.values() ) {
populateStaticMetamodel( embeddable );
}
}
}
private <X> void applyIdMetadata(PersistentClass persistentClass, EntityTypeImpl<X> jpaEntityType) {
if ( persistentClass.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = persistentClass.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
jpaEntityType.getBuilder().applyIdAttribute(
attributeFactory.buildIdAttribute( jpaEntityType, declaredIdentifierProperty )
);
}
}
else if ( persistentClass.hasIdentifierMapper() ) {
@SuppressWarnings("unchecked")
Iterator<Property> propertyIterator = persistentClass.getIdentifierMapper().getPropertyIterator();
Set<SingularAttribute<? super X, ?>> attributes = buildIdClassAttributes( jpaEntityType, propertyIterator );
jpaEntityType.getBuilder().applyIdClassAttributes( attributes );
}
else {
final KeyValue value = persistentClass.getIdentifier();
if ( value instanceof Component ) {
final Component component = (Component) value;
if ( component.getPropertySpan() > 1 ) {
}
else {
jpaEntityType.getBuilder().applyIdAttribute(
attributeFactory.buildIdAttribute(
jpaEntityType,
(Property) component.getPropertyIterator().next()
)
);
}
}
}
}
private <X> void applyIdMetadata(MappedSuperclass mappingType, MappedSuperclassTypeImpl<X> jpaMappingType) {
if ( mappingType.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
jpaMappingType.getBuilder().applyIdAttribute(
attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty )
);
}
}
else if ( mappingType.getIdentifierMapper() != null ) {
@SuppressWarnings("unchecked")
Iterator<Property> propertyIterator = mappingType.getIdentifierMapper().getPropertyIterator();
Set<SingularAttribute<? super X, ?>> attributes = buildIdClassAttributes(
jpaMappingType,
propertyIterator
);
jpaMappingType.getBuilder().applyIdClassAttributes( attributes );
}
}
private <X> void applyVersionAttribute(PersistentClass persistentClass, EntityTypeImpl<X> jpaEntityType) {
final Property declaredVersion = persistentClass.getDeclaredVersion();
if ( declaredVersion != null ) {
jpaEntityType.getBuilder().applyVersionAttribute(
attributeFactory.buildVersionAttribute( jpaEntityType, declaredVersion )
);
}
}
private <X> void applyVersionAttribute(MappedSuperclass mappingType, MappedSuperclassTypeImpl<X> jpaMappingType) {
final Property declaredVersion = mappingType.getDeclaredVersion();
if ( declaredVersion != null ) {
jpaMappingType.getBuilder().applyVersionAttribute(
attributeFactory.buildVersionAttribute( jpaMappingType, declaredVersion )
);
}
}
private <X> Set<SingularAttribute<? super X, ?>> buildIdClassAttributes(
AbstractIdentifiableType<X> ownerType,
Iterator<Property> propertyIterator) {
if ( LOG.isTraceEnabled() ) {
LOG.trace( "Building old-school composite identifier [" + ownerType.getJavaType().getName() + ']' );
}
Set<SingularAttribute<? super X, ?>> attributes = new HashSet<SingularAttribute<? super X, ?>>();
while ( propertyIterator.hasNext() ) {
attributes.add( attributeFactory.buildIdAttribute( ownerType, propertyIterator.next() ) );
}
return attributes;
}
private <X> void populateStaticMetamodel(AbstractManagedType<X> managedType) {
final Class<X> managedTypeClass = managedType.getJavaType();
if ( managedTypeClass == null ) {
return;
}
final String metamodelClassName = managedTypeClass.getName() + '_';
try {
final Class metamodelClass = Class.forName( metamodelClassName, true, managedTypeClass.getClassLoader() );
registerAttributes( metamodelClass, managedType );
}
catch (ClassNotFoundException ignore) {
}
AbstractManagedType<? super X> superType = managedType.getSupertype();
if ( superType != null ) {
populateStaticMetamodel( superType );
}
}
private final Set<Class> processedMetamodelClasses = new HashSet<Class>();
private <X> void registerAttributes(Class metamodelClass, AbstractManagedType<X> managedType) {
if ( !processedMetamodelClasses.add( metamodelClass ) ) {
return;
}
for ( Attribute<X, ?> attribute : managedType.getDeclaredAttributes() ) {
registerAttribute( metamodelClass, attribute );
}
if ( IdentifiableType.class.isInstance( managedType ) ) {
final AbstractIdentifiableType<X> entityType = (AbstractIdentifiableType<X>) managedType;
if ( entityType.hasDeclaredVersionAttribute() ) {
registerAttribute( metamodelClass, entityType.getDeclaredVersion() );
}
if ( entityType.hasIdClass() ) {
final Set<SingularAttribute<? super X, ?>> attributes = entityType.getIdClassAttributesSafely();
if ( attributes != null ) {
for ( SingularAttribute<? super X, ?> attribute : attributes ) {
registerAttribute( metamodelClass, attribute );
}
}
}
}
}
private <X> void registerAttribute(Class metamodelClass, Attribute<X, ?> attribute) {
final String name = attribute.getName();
try {
final boolean allowNonDeclaredFieldReference =
attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED
|| attribute.getDeclaringType().getPersistenceType() == Type.PersistenceType.EMBEDDABLE;
final Field field = allowNonDeclaredFieldReference
? metamodelClass.getField( name )
: metamodelClass.getDeclaredField( name );
try {
ReflectHelper.ensureAccessibility( field );
field.set( null, attribute );
}
catch (IllegalAccessException e) {
throw new AssertionFailure(
"Unable to inject static metamodel attribute : " + metamodelClass.getName() + '#' + name,
e
);
}
catch (IllegalArgumentException e) {
LOG.illegalArgumentOnStaticMetamodelFieldInjection(
metamodelClass.getName(),
name,
attribute.getClass().getName(),
field.getType().getName()
);
}
}
catch (NoSuchFieldException e) {
LOG.unableToLocateStaticMetamodelField( metamodelClass.getName(), name );
}
}
public MappedSuperclassTypeImpl<?> locateMappedSuperclassType(MappedSuperclass mappedSuperclass) {
return mappedSuperclassByMappedSuperclassMapping.get( mappedSuperclass );
}
public void pushEntityWorkedOn(PersistentClass persistentClass) {
stackOfPersistentClassesBeingProcessed.add( persistentClass );
}
public void popEntityWorkedOn(PersistentClass persistentClass) {
final PersistentClass stackTop = stackOfPersistentClassesBeingProcessed.remove(
stackOfPersistentClassesBeingProcessed.size() - 1
);
if ( stackTop != persistentClass ) {
throw new AssertionFailure(
"Inconsistent popping: "
+ persistentClass.getEntityName() + " instead of " + stackTop.getEntityName()
);
}
}
private PersistentClass getEntityWorkedOn() {
return stackOfPersistentClassesBeingProcessed.get(
stackOfPersistentClassesBeingProcessed.size() - 1
);
}
public PersistentClass getPersistentClassHostingProperties(MappedSuperclassTypeImpl<?> mappedSuperclassType) {
final PersistentClass persistentClass = mappedSuperClassTypeToPersistentClass.get( mappedSuperclassType );
if ( persistentClass == null ) {
throw new AssertionFailure(
"Could not find PersistentClass for MappedSuperclassType: "
+ mappedSuperclassType.getJavaType()
);
}
return persistentClass;
}
public Set<MappedSuperclass> getUnusedMappedSuperclasses() {
return new HashSet<MappedSuperclass>( knownMappedSuperclasses );
}
}