package org.hibernate.metamodel.binding;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.internal.util.collections.JoinedIterable;
import org.hibernate.metamodel.domain.AttributeContainer;
import org.hibernate.metamodel.domain.Entity;
import org.hibernate.metamodel.domain.PluralAttribute;
import org.hibernate.metamodel.domain.PluralAttributeNature;
import org.hibernate.metamodel.domain.SingularAttribute;
import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.source.MetaAttributeContext;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityTuplizer;
public class EntityBinding implements AttributeBindingContainer {
private static final String NULL_DISCRIMINATOR_MATCH_VALUE = "null";
private static final String NOT_NULL_DISCRIMINATOR_MATCH_VALUE = "not null";
private final EntityBinding superEntityBinding;
private final List<EntityBinding> subEntityBindings = new ArrayList<EntityBinding>();
private final HierarchyDetails hierarchyDetails;
private Entity entity;
private TableSpecification primaryTable;
private String primaryTableName;
private Map<String, TableSpecification> secondaryTables = new HashMap<String, TableSpecification>();
private ValueHolder<Class<?>> proxyInterfaceType;
private String jpaEntityName;
private Class<? extends EntityPersister> customEntityPersisterClass;
private Class<? extends EntityTuplizer> customEntityTuplizerClass;
private String discriminatorMatchValue;
private Set<FilterDefinition> filterDefinitions = new HashSet<FilterDefinition>();
private Set<SingularAssociationAttributeBinding> entityReferencingAttributeBindings = new HashSet<SingularAssociationAttributeBinding>();
private MetaAttributeContext metaAttributeContext;
private boolean lazy;
private boolean mutable;
private String whereFilter;
private String rowId;
private boolean dynamicUpdate;
private boolean dynamicInsert;
private int batchSize;
private boolean selectBeforeUpdate;
private boolean hasSubselectLoadableCollections;
private Boolean isAbstract;
private String customLoaderName;
private CustomSQL customInsert;
private CustomSQL customUpdate;
private CustomSQL customDelete;
private Set<String> synchronizedTableNames = new HashSet<String>();
private Map<String, AttributeBinding> attributeBindingMap = new HashMap<String, AttributeBinding>();
private List<JpaCallbackClass> jpaCallbackClasses = new ArrayList<JpaCallbackClass>();
public EntityBinding(InheritanceType inheritanceType, EntityMode entityMode) {
this.superEntityBinding = null;
this.hierarchyDetails = new HierarchyDetails( this, inheritanceType, entityMode );
}
public EntityBinding(EntityBinding superEntityBinding) {
this.superEntityBinding = superEntityBinding;
this.superEntityBinding.subEntityBindings.add( this );
this.hierarchyDetails = superEntityBinding.getHierarchyDetails();
}
public HierarchyDetails getHierarchyDetails() {
return hierarchyDetails;
}
public EntityBinding getSuperEntityBinding() {
return superEntityBinding;
}
public boolean isRoot() {
return superEntityBinding == null;
}
public boolean isPolymorphic() {
return superEntityBinding != null ||
hierarchyDetails.getEntityDiscriminator() != null ||
! subEntityBindings.isEmpty();
}
public boolean hasSubEntityBindings() {
return subEntityBindings.size() > 0;
}
public int getSubEntityBindingClosureSpan() {
int n = subEntityBindings.size();
for ( EntityBinding subEntityBinding : subEntityBindings ) {
n += subEntityBinding.getSubEntityBindingClosureSpan();
}
return n;
}
public Iterable<EntityBinding> getDirectSubEntityBindings() {
return subEntityBindings;
}
public Iterable<EntityBinding> getPostOrderSubEntityBindingClosure() {
List<Iterable<EntityBinding>> subclassIterables = new ArrayList<Iterable<EntityBinding>>( subEntityBindings.size() + 1 );
for ( EntityBinding subEntityBinding : subEntityBindings ) {
Iterable<EntityBinding> subSubEntityBindings = subEntityBinding.getPostOrderSubEntityBindingClosure();
if ( subSubEntityBindings.iterator().hasNext() ) {
subclassIterables.add( subSubEntityBindings );
}
}
if ( ! subEntityBindings.isEmpty() ) {
subclassIterables.add( subEntityBindings );
}
return new JoinedIterable<EntityBinding>( subclassIterables );
}
public Iterable<EntityBinding> getPreOrderSubEntityBindingClosure() {
return getPreOrderSubEntityBindingClosure( false );
}
private Iterable<EntityBinding> getPreOrderSubEntityBindingClosure(boolean includeThis) {
List<Iterable<EntityBinding>> iterables = new ArrayList<Iterable<EntityBinding>>();
if ( includeThis ) {
iterables.add( java.util.Collections.singletonList( this ) );
}
for ( EntityBinding subEntityBinding : subEntityBindings ) {
Iterable<EntityBinding> subSubEntityBindingClosure = subEntityBinding.getPreOrderSubEntityBindingClosure( true );
if ( subSubEntityBindingClosure.iterator().hasNext() ) {
iterables.add( subSubEntityBindingClosure );
}
}
return new JoinedIterable<EntityBinding>( iterables );
}
public Entity getEntity() {
return entity;
}
public void setEntity(Entity entity) {
this.entity = entity;
}
public TableSpecification getPrimaryTable() {
return primaryTable;
}
public void setPrimaryTable(TableSpecification primaryTable) {
this.primaryTable = primaryTable;
}
public TableSpecification locateTable(String tableName) {
if ( tableName == null || tableName.equals( getPrimaryTableName() ) ) {
return primaryTable;
}
TableSpecification tableSpec = secondaryTables.get( tableName );
if ( tableSpec == null ) {
throw new AssertionFailure(
String.format(
"Unable to find table %s amongst tables %s",
tableName,
secondaryTables.keySet()
)
);
}
return tableSpec;
}
public String getPrimaryTableName() {
return primaryTableName;
}
public void setPrimaryTableName(String primaryTableName) {
this.primaryTableName = primaryTableName;
}
public void addSecondaryTable(String tableName, TableSpecification table) {
secondaryTables.put( tableName, table );
}
public boolean isVersioned() {
return getHierarchyDetails().getVersioningAttributeBinding() != null;
}
public boolean isDiscriminatorMatchValueNull() {
return NULL_DISCRIMINATOR_MATCH_VALUE.equals( discriminatorMatchValue );
}
public boolean isDiscriminatorMatchValueNotNull() {
return NOT_NULL_DISCRIMINATOR_MATCH_VALUE.equals( discriminatorMatchValue );
}
public String getDiscriminatorMatchValue() {
return discriminatorMatchValue;
}
public void setDiscriminatorMatchValue(String discriminatorMatchValue) {
this.discriminatorMatchValue = discriminatorMatchValue;
}
public Iterable<FilterDefinition> getFilterDefinitions() {
return filterDefinitions;
}
public void addFilterDefinition(FilterDefinition filterDefinition) {
filterDefinitions.add( filterDefinition );
}
public Iterable<SingularAssociationAttributeBinding> getEntityReferencingAttributeBindings() {
return entityReferencingAttributeBindings;
}
@Override
public EntityBinding seekEntityBinding() {
return this;
}
@Override
public String getPathBase() {
return getEntity().getName();
}
@Override
public Class<?> getClassReference() {
return getEntity().getClassReference();
}
@Override
public AttributeContainer getAttributeContainer() {
return getEntity();
}
protected void registerAttributeBinding(String name, AttributeBinding attributeBinding) {
if ( SingularAssociationAttributeBinding.class.isInstance( attributeBinding ) ) {
entityReferencingAttributeBindings.add( (SingularAssociationAttributeBinding) attributeBinding );
}
attributeBindingMap.put( name, attributeBinding );
}
@Override
public MetaAttributeContext getMetaAttributeContext() {
return metaAttributeContext;
}
public void setMetaAttributeContext(MetaAttributeContext metaAttributeContext) {
this.metaAttributeContext = metaAttributeContext;
}
public boolean isMutable() {
return mutable;
}
public void setMutable(boolean mutable) {
this.mutable = mutable;
}
public boolean isLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
public ValueHolder<Class<?>> getProxyInterfaceType() {
return proxyInterfaceType;
}
public void setProxyInterfaceType(ValueHolder<Class<?>> proxyInterfaceType) {
this.proxyInterfaceType = proxyInterfaceType;
}
public String getWhereFilter() {
return whereFilter;
}
public void setWhereFilter(String whereFilter) {
this.whereFilter = whereFilter;
}
public String getRowId() {
return rowId;
}
public void setRowId(String rowId) {
this.rowId = rowId;
}
public boolean isDynamicUpdate() {
return dynamicUpdate;
}
public void setDynamicUpdate(boolean dynamicUpdate) {
this.dynamicUpdate = dynamicUpdate;
}
public boolean isDynamicInsert() {
return dynamicInsert;
}
public void setDynamicInsert(boolean dynamicInsert) {
this.dynamicInsert = dynamicInsert;
}
public int getBatchSize() {
return batchSize;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
public boolean isSelectBeforeUpdate() {
return selectBeforeUpdate;
}
public void setSelectBeforeUpdate(boolean selectBeforeUpdate) {
this.selectBeforeUpdate = selectBeforeUpdate;
}
public boolean hasSubselectLoadableCollections() {
return hasSubselectLoadableCollections;
}
void setSubselectLoadableCollections(boolean hasSubselectLoadableCollections) {
this.hasSubselectLoadableCollections = hasSubselectLoadableCollections;
}
public Class<? extends EntityPersister> getCustomEntityPersisterClass() {
return customEntityPersisterClass;
}
public void setCustomEntityPersisterClass(Class<? extends EntityPersister> customEntityPersisterClass) {
this.customEntityPersisterClass = customEntityPersisterClass;
}
public Class<? extends EntityTuplizer> getCustomEntityTuplizerClass() {
return customEntityTuplizerClass;
}
public void setCustomEntityTuplizerClass(Class<? extends EntityTuplizer> customEntityTuplizerClass) {
this.customEntityTuplizerClass = customEntityTuplizerClass;
}
public Boolean isAbstract() {
return isAbstract;
}
public void setAbstract(Boolean isAbstract) {
this.isAbstract = isAbstract;
}
public Set<String> getSynchronizedTableNames() {
return synchronizedTableNames;
}
public void addSynchronizedTableNames(java.util.Collection<String> synchronizedTableNames) {
this.synchronizedTableNames.addAll( synchronizedTableNames );
}
public String getJpaEntityName() {
return jpaEntityName;
}
public void setJpaEntityName(String jpaEntityName) {
this.jpaEntityName = jpaEntityName;
}
public String getCustomLoaderName() {
return customLoaderName;
}
public void setCustomLoaderName(String customLoaderName) {
this.customLoaderName = customLoaderName;
}
public CustomSQL getCustomInsert() {
return customInsert;
}
public void setCustomInsert(CustomSQL customInsert) {
this.customInsert = customInsert;
}
public CustomSQL getCustomUpdate() {
return customUpdate;
}
public void setCustomUpdate(CustomSQL customUpdate) {
this.customUpdate = customUpdate;
}
public CustomSQL getCustomDelete() {
return customDelete;
}
public void setCustomDelete(CustomSQL customDelete) {
this.customDelete = customDelete;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "EntityBinding" );
sb.append( "{entity=" ).append( entity != null ? entity.getName() : "not set" );
sb.append( '}' );
return sb.toString();
}
@Override
public BasicAttributeBinding makeBasicAttributeBinding(SingularAttribute attribute) {
return makeSimpleAttributeBinding( attribute, false, false );
}
private BasicAttributeBinding makeSimpleAttributeBinding(SingularAttribute attribute, boolean forceNonNullable, boolean forceUnique) {
final BasicAttributeBinding binding = new BasicAttributeBinding(
this,
attribute,
forceNonNullable,
forceUnique
);
registerAttributeBinding( attribute.getName(), binding );
return binding;
}
@Override
public ComponentAttributeBinding makeComponentAttributeBinding(SingularAttribute attribute) {
final ComponentAttributeBinding binding = new ComponentAttributeBinding( this, attribute );
registerAttributeBinding( attribute.getName(), binding );
return binding;
}
@Override
public ManyToOneAttributeBinding makeManyToOneAttributeBinding(SingularAttribute attribute) {
final ManyToOneAttributeBinding binding = new ManyToOneAttributeBinding( this, attribute );
registerAttributeBinding( attribute.getName(), binding );
return binding;
}
@Override
public BagBinding makeBagAttributeBinding(PluralAttribute attribute, CollectionElementNature nature) {
Helper.checkPluralAttributeNature( attribute, PluralAttributeNature.BAG );
final BagBinding binding = new BagBinding( this, attribute, nature );
registerAttributeBinding( attribute.getName(), binding );
return binding;
}
@Override
public SetBinding makeSetAttributeBinding(PluralAttribute attribute, CollectionElementNature nature) {
Helper.checkPluralAttributeNature( attribute, PluralAttributeNature.SET );
final SetBinding binding = new SetBinding( this, attribute, nature );
registerAttributeBinding( attribute.getName(), binding );
return binding;
}
@Override
public AttributeBinding locateAttributeBinding(String name) {
return attributeBindingMap.get( name );
}
@Override
public Iterable<AttributeBinding> attributeBindings() {
return attributeBindingMap.values();
}
public int getAttributeBindingClosureSpan() {
return superEntityBinding != null ?
superEntityBinding.getAttributeBindingClosureSpan() + attributeBindingMap.size() :
attributeBindingMap.size();
}
public Iterable<AttributeBinding> getAttributeBindingClosure() {
Iterable<AttributeBinding> iterable;
if ( superEntityBinding != null ) {
List<Iterable<AttributeBinding>> iterables = new ArrayList<Iterable<AttributeBinding>>( 2 );
iterables.add( superEntityBinding.getAttributeBindingClosure() );
iterables.add( attributeBindings() );
iterable = new JoinedIterable<AttributeBinding>( iterables );
}
else {
iterable = attributeBindings();
}
return iterable;
}
public Iterable<AttributeBinding> getSubEntityAttributeBindingClosure() {
List<Iterable<AttributeBinding>> iterables = new ArrayList<Iterable<AttributeBinding>>();
iterables.add( getAttributeBindingClosure() );
for ( EntityBinding subEntityBinding : getPreOrderSubEntityBindingClosure() ) {
iterables.add( subEntityBinding.attributeBindings() );
}
return new JoinedIterable<AttributeBinding>( iterables );
}
public void setJpaCallbackClasses( List<JpaCallbackClass> jpaCallbackClasses ) {
this.jpaCallbackClasses = jpaCallbackClasses;
}
public Iterable<JpaCallbackClass> getJpaCallbackClasses() {
return jpaCallbackClasses;
}
}