package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.Access;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.mapping.PersistentClass;
public class InheritanceState {
private XClass clazz;
private boolean hasSiblings = false;
private boolean hasParents = false;
private InheritanceType type;
private boolean isEmbeddableSuperclass = false;
private Map<XClass, InheritanceState> inheritanceStatePerClass;
private List<XClass> classesToProcessForMappedSuperclass = new ArrayList<XClass>();
private MetadataBuildingContext buildingContext;
private AccessType accessType;
private ElementsToProcess elementsToProcess;
private Boolean hasIdClassOrEmbeddedId;
public InheritanceState(
XClass clazz,
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext buildingContext) {
this.setClazz( clazz );
this.buildingContext = buildingContext;
this.inheritanceStatePerClass = inheritanceStatePerClass;
extractInheritanceType();
}
private void () {
XAnnotatedElement element = getClazz();
Inheritance inhAnn = element.getAnnotation( Inheritance.class );
MappedSuperclass mappedSuperClass = element.getAnnotation( MappedSuperclass.class );
if ( mappedSuperClass != null ) {
setEmbeddableSuperclass( true );
setType( inhAnn == null ? null : inhAnn.strategy() );
}
else {
setType( inhAnn == null ? InheritanceType.SINGLE_TABLE : inhAnn.strategy() );
}
}
boolean hasTable() {
return !hasParents() || !InheritanceType.SINGLE_TABLE.equals( getType() );
}
boolean hasDenormalizedTable() {
return hasParents() && InheritanceType.TABLE_PER_CLASS.equals( getType() );
}
public static InheritanceState getInheritanceStateOfSuperEntity(
XClass clazz, Map<XClass, InheritanceState> states
) {
XClass superclass = clazz;
do {
superclass = superclass.getSuperclass();
InheritanceState currentState = states.get( superclass );
if ( currentState != null && !currentState.isEmbeddableSuperclass() ) {
return currentState;
}
}
while ( superclass != null && !Object.class.getName().equals( superclass.getName() ) );
return null;
}
public static InheritanceState getSuperclassInheritanceState(XClass clazz, Map<XClass, InheritanceState> states) {
XClass superclass = clazz;
do {
superclass = superclass.getSuperclass();
InheritanceState currentState = states.get( superclass );
if ( currentState != null ) {
return currentState;
}
}
while ( superclass != null && !Object.class.getName().equals( superclass.getName() ) );
return null;
}
public XClass getClazz() {
return clazz;
}
public void setClazz(XClass clazz) {
this.clazz = clazz;
}
public boolean hasSiblings() {
return hasSiblings;
}
public void setHasSiblings(boolean hasSiblings) {
this.hasSiblings = hasSiblings;
}
public boolean hasParents() {
return hasParents;
}
public void setHasParents(boolean hasParents) {
this.hasParents = hasParents;
}
public InheritanceType getType() {
return type;
}
public void setType(InheritanceType type) {
this.type = type;
}
public boolean isEmbeddableSuperclass() {
return isEmbeddableSuperclass;
}
public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
isEmbeddableSuperclass = embeddableSuperclass;
}
void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
getElementsToProcess();
addMappedSuperClassInMetadata( persistenceClass );
entityBinder.setPropertyAccessType( accessType );
}
public XClass getClassWithIdClass(boolean evenIfSubclass) {
if ( !evenIfSubclass && hasParents() ) {
return null;
}
if ( clazz.isAnnotationPresent( IdClass.class ) ) {
return clazz;
}
else {
InheritanceState state = InheritanceState.getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
if ( state != null ) {
return state.getClassWithIdClass( true );
}
else {
return null;
}
}
}
public Boolean hasIdClassOrEmbeddedId() {
if ( hasIdClassOrEmbeddedId == null ) {
hasIdClassOrEmbeddedId = false;
if ( getClassWithIdClass( true ) != null ) {
hasIdClassOrEmbeddedId = true;
}
else {
final ElementsToProcess process = getElementsToProcess();
for ( PropertyData property : process.getElements() ) {
if ( property.getProperty().isAnnotationPresent( EmbeddedId.class ) ) {
hasIdClassOrEmbeddedId = true;
break;
}
}
}
}
return hasIdClassOrEmbeddedId;
}
public ElementsToProcess getElementsToProcess() {
if ( elementsToProcess == null ) {
InheritanceState inheritanceState = inheritanceStatePerClass.get( clazz );
assert !inheritanceState.isEmbeddableSuperclass();
getMappedSuperclassesTillNextEntityOrdered();
accessType = determineDefaultAccessType();
ArrayList<PropertyData> elements = new ArrayList<PropertyData>();
int idPropertyCount = 0;
for ( XClass classToProcessForMappedSuperclass : classesToProcessForMappedSuperclass ) {
PropertyContainer propertyContainer = new PropertyContainer(
classToProcessForMappedSuperclass,
clazz,
accessType
);
int currentIdPropertyCount = AnnotationBinder.addElementsOfClass(
elements,
propertyContainer,
buildingContext
);
idPropertyCount += currentIdPropertyCount;
}
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() );
}
elements.trimToSize();
elementsToProcess = new ElementsToProcess( elements, idPropertyCount );
}
return elementsToProcess;
}
private AccessType determineDefaultAccessType() {
for (XClass xclass = clazz; xclass != null; xclass = xclass.getSuperclass()) {
if ( ( xclass.getSuperclass() == null || Object.class.getName().equals( xclass.getSuperclass().getName() ) )
&& ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) )
&& xclass.isAnnotationPresent( Access.class ) ) {
return AccessType.getAccessStrategy( xclass.getAnnotation( Access.class ).value() );
}
}
for (XClass xclass = clazz; xclass != null && !Object.class.getName().equals(xclass.getName()); xclass = xclass.getSuperclass()) {
if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
return AccessType.PROPERTY;
}
}
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
return AccessType.FIELD;
}
}
}
}
throw new AnnotationException( "No identifier specified for entity: " + clazz );
}
private void getMappedSuperclassesTillNextEntityOrdered() {
XClass currentClassInHierarchy = clazz;
InheritanceState superclassState;
do {
classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );
XClass superClass = currentClassInHierarchy;
do {
superClass = superClass.getSuperclass();
superclassState = inheritanceStatePerClass.get( superClass );
}
while ( superClass != null
&& !buildingContext.getBootstrapContext().getReflectionManager().equals( superClass, Object.class )
&& superclassState == null );
currentClassInHierarchy = superClass;
}
while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
}
private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
final InheritanceState superEntityState =
InheritanceState.getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
PersistentClass superEntity =
superEntityState != null ?
buildingContext.getMetadataCollector().getEntityBinding( superEntityState.getClazz().getName() ) :
null;
final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
for ( int index = 0; index < lastMappedSuperclass; index++ ) {
org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager()
.toClass( classesToProcessForMappedSuperclass.get( index ) );
mappedSuperclass = buildingContext.getMetadataCollector().getMappedSuperclass( type );
if ( mappedSuperclass == null ) {
mappedSuperclass = new org.hibernate.mapping.MappedSuperclass( parentSuperclass, superEntity );
mappedSuperclass.setMappedClass( type );
buildingContext.getMetadataCollector().addMappedSuperclass( type, mappedSuperclass );
}
}
if ( mappedSuperclass != null ) {
persistentClass.setSuperMappedSuperclass( mappedSuperclass );
}
}
static final class ElementsToProcess {
private final List<PropertyData> properties;
private final int idPropertyCount;
public List<PropertyData> getElements() {
return properties;
}
public int getIdPropertyCount() {
return idPropertyCount;
}
private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
this.properties = properties;
this.idPropertyCount = idPropertyCount;
}
}
}