package org.hibernate.persister.walking.spi;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
public class MetamodelGraphWalker {
private static final Logger log = Logger.getLogger( MetamodelGraphWalker.class );
public static void visitEntity(AssociationVisitationStrategy strategy, EntityPersister persister) {
strategy.start();
try {
new MetamodelGraphWalker( strategy, persister.getFactory() )
.visitEntityDefinition( persister );
}
finally {
strategy.finish();
}
}
public static void visitCollection(AssociationVisitationStrategy strategy, CollectionPersister persister) {
strategy.start();
try {
new MetamodelGraphWalker( strategy, persister.getFactory() )
.visitCollectionDefinition( persister );
}
finally {
strategy.finish();
}
}
private final AssociationVisitationStrategy strategy;
private final SessionFactoryImplementor factory;
private PropertyPath currentPropertyPath = new PropertyPath();
public MetamodelGraphWalker(AssociationVisitationStrategy strategy, SessionFactoryImplementor factory) {
this.strategy = strategy;
this.factory = factory;
}
private void visitEntityDefinition(EntityDefinition entityDefinition) {
strategy.startingEntity( entityDefinition );
AbstractEntityPersister persister = (AbstractEntityPersister) entityDefinition.getEntityPersister();
visitIdentifierDefinition( entityDefinition.getEntityKeyDefinition() );
visitAttributes( entityDefinition, persister);
strategy.finishingEntity( entityDefinition );
}
private void visitIdentifierDefinition(EntityIdentifierDefinition identifierDefinition) {
strategy.startingEntityIdentifier( identifierDefinition );
if ( identifierDefinition.isEncapsulated() ) {
final EncapsulatedEntityIdentifierDefinition idAsEncapsulated = (EncapsulatedEntityIdentifierDefinition) identifierDefinition;
final AttributeDefinition idAttr = idAsEncapsulated.getAttributeDefinition();
if ( CompositionDefinition.class.isInstance( idAttr ) ) {
visitCompositeDefinition( (CompositionDefinition) idAttr );
}
}
else {
visitCompositeDefinition( (NonEncapsulatedEntityIdentifierDefinition) identifierDefinition );
}
strategy.finishingEntityIdentifier( identifierDefinition );
}
private void visitAttributes(AttributeSource attributeSource, AbstractEntityPersister sourcePersister) {
final Iterable<AttributeDefinition> attributeDefinitions = attributeSource.getAttributes();
if ( attributeDefinitions == null ) {
return;
}
for ( AttributeDefinition attributeDefinition : attributeDefinitions ) {
visitAttributeDefinition( attributeDefinition, sourcePersister);
}
}
private void visitAttributeDefinition(AttributeDefinition attributeDefinition, AbstractEntityPersister sourcePersister) {
final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() );
log.debug( "Visiting attribute path : " + subPath.getFullPath() );
if ( attributeDefinition.getType().isAssociationType() ) {
final AssociationAttributeDefinition associationAttributeDefinition =
(AssociationAttributeDefinition) attributeDefinition;
final AssociationKey associationKey = associationAttributeDefinition.getAssociationKey();
if ( isDuplicateAssociationKey( associationKey ) ) {
log.debug( "Property path deemed to be circular : " + subPath.getFullPath() );
strategy.foundCircularAssociation( associationAttributeDefinition );
return;
}
if ( sourcePersister != null ) {
String[] columns = sourcePersister.toColumns(attributeDefinition.getName());
if ( columns.length == 0 ) {
return;
}
}
}
boolean continueWalk = strategy.startingAttribute( attributeDefinition );
if ( continueWalk ) {
final PropertyPath old = currentPropertyPath;
currentPropertyPath = subPath;
try {
final Type attributeType = attributeDefinition.getType();
if ( attributeType.isAssociationType() ) {
visitAssociation( (AssociationAttributeDefinition) attributeDefinition );
}
else if ( attributeType.isComponentType() ) {
visitCompositeDefinition( (CompositionDefinition) attributeDefinition );
}
}
finally {
currentPropertyPath = old;
}
}
strategy.finishingAttribute( attributeDefinition );
}
private void visitAssociation(AssociationAttributeDefinition attribute) {
addAssociationKey( attribute.getAssociationKey() );
final AssociationAttributeDefinition.AssociationNature nature = attribute.getAssociationNature();
if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) {
visitAnyDefinition( attribute.toAnyDefinition() );
}
else if ( nature == AssociationAttributeDefinition.AssociationNature.COLLECTION ) {
visitCollectionDefinition( attribute.toCollectionDefinition() );
}
else {
visitEntityDefinition( attribute.toEntityDefinition() );
}
}
private void visitAnyDefinition(AnyMappingDefinition anyDefinition) {
strategy.foundAny( anyDefinition );
}
private void visitCompositeDefinition(CompositionDefinition compositionDefinition) {
strategy.startingComposite( compositionDefinition );
visitAttributes( compositionDefinition, null );
strategy.finishingComposite( compositionDefinition );
}
private void visitCollectionDefinition(CollectionDefinition collectionDefinition) {
strategy.startingCollection( collectionDefinition );
visitCollectionIndex( collectionDefinition );
visitCollectionElements( collectionDefinition );
strategy.finishingCollection( collectionDefinition );
}
private void visitCollectionIndex(CollectionDefinition collectionDefinition) {
final CollectionIndexDefinition collectionIndexDefinition = collectionDefinition.getIndexDefinition();
if ( collectionIndexDefinition == null ) {
return;
}
strategy.startingCollectionIndex( collectionIndexDefinition );
log.debug( "Visiting index for collection : " + currentPropertyPath.getFullPath() );
currentPropertyPath = currentPropertyPath.append( "<index>" );
try {
final Type collectionIndexType = collectionIndexDefinition.getType();
if ( collectionIndexType.isAnyType() ) {
visitAnyDefinition( collectionIndexDefinition.toAnyMappingDefinition() );
}
else if ( collectionIndexType.isComponentType() ) {
visitCompositeDefinition( collectionIndexDefinition.toCompositeDefinition() );
}
else if ( collectionIndexType.isAssociationType() ) {
visitEntityDefinition( collectionIndexDefinition.toEntityDefinition() );
}
}
finally {
currentPropertyPath = currentPropertyPath.getParent();
}
strategy.finishingCollectionIndex( collectionIndexDefinition );
}
private void visitCollectionElements(CollectionDefinition collectionDefinition) {
final CollectionElementDefinition elementDefinition = collectionDefinition.getElementDefinition();
strategy.startingCollectionElements( elementDefinition );
final Type collectionElementType = elementDefinition.getType();
if ( collectionElementType.isAnyType() ) {
visitAnyDefinition( elementDefinition.toAnyMappingDefinition() );
}
else if ( collectionElementType.isComponentType() ) {
visitCompositeDefinition( elementDefinition.toCompositeElementDefinition() );
}
else if ( collectionElementType.isEntityType() ) {
if ( ! collectionDefinition.getCollectionPersister().isOneToMany() ) {
final QueryableCollection queryableCollection = (QueryableCollection) collectionDefinition.getCollectionPersister();
addAssociationKey(
new AssociationKey(
queryableCollection.getTableName(),
queryableCollection.getElementColumnNames()
)
);
}
visitEntityDefinition( elementDefinition.toEntityDefinition() );
}
strategy.finishingCollectionElements( elementDefinition );
}
private final Set<AssociationKey> visitedAssociationKeys = new HashSet<AssociationKey>();
protected void addAssociationKey(AssociationKey associationKey) {
if ( ! visitedAssociationKeys.add( associationKey ) ) {
throw new WalkingException(
String.format( "Association has already been visited: %s", associationKey )
);
}
strategy.associationKeyRegistered( associationKey );
}
protected boolean isDuplicateAssociationKey(AssociationKey associationKey) {
return visitedAssociationKeys.contains( associationKey ) || strategy.isDuplicateAssociationKey( associationKey );
}
}