All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.boot.model.source.internal.hbm.EntityHierarchyBuilder Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.boot.model.source.internal.hbm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmDiscriminatorSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmEntityBaseDefinition;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmJoinedSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmSubclassEntityBaseDefinition;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmUnionSubclassEntityType;
import org.hibernate.boot.model.source.spi.EntitySource;
import org.hibernate.internal.util.StringHelper;

import org.jboss.logging.Logger;

/**
 * Helper for dealing with entity inheritance hierarchies in {@code hbm.xml}
 * processing.  In {@code hbm.xml} the super/sub may be split across documents.
 * The purpose of this class is to:
    *
  1. * validate that all hierarchies are complete (make sure a mapping does not reference * an unknown entity as its super) *
  2. *
  3. * ultimately order the processing of every entity to make sure we process each * hierarchy "downward" (from super to sub(s)). *
  4. *
* * @author Steve Ebersole */ public class EntityHierarchyBuilder { private static final Logger log = Logger.getLogger( EntityHierarchyBuilder.class ); private final List entityHierarchyList = new ArrayList(); private final Map entitySourceByNameMap = new HashMap(); private Map> toBeLinkedQueue; public EntityHierarchyBuilder() { } /** * To be called after all mapping documents have been processed (via {@link #indexMappingDocument}) * * @return The built hierarchies * * @throws HibernateException Indicates subclass mappings still waiting to be linked to their super types */ public List buildHierarchies() throws HibernateException { if ( toBeLinkedQueue != null && !toBeLinkedQueue.isEmpty() ) { if ( log.isDebugEnabled() ) { for ( Map.Entry> waitingListEntry : toBeLinkedQueue.entrySet() ) { for ( ExtendsQueueEntry waitingEntry : waitingListEntry.getValue() ) { log.debugf( "Entity super-type named as extends [%s] for subclass [%s:%s] not found", waitingListEntry.getKey(), waitingEntry.sourceMappingDocument.getOrigin(), waitingEntry.sourceMappingDocument.determineEntityName( waitingEntry.jaxbSubEntityMapping ) ); } } } throw new HibernateException( "Not all named super-types (extends) were found : " + toBeLinkedQueue.keySet() ); } return entityHierarchyList; } /** * Called for each mapping document. * * @param mappingDocument The {@code hbm.xml} document to index */ public void indexMappingDocument(MappingDocument mappingDocument) { log.tracef( "Indexing mapping document [%s] for purpose of building entity hierarchy ordering", mappingDocument.getOrigin() ); final JaxbHbmHibernateMapping mappingBinding = mappingDocument.getDocumentRoot(); // iterate all root class definitions at the hibernate-mapping level for ( JaxbHbmRootEntityType jaxbRootEntity : mappingBinding.getClazz() ) { // we can immediately handle elements in terms of creating the hierarchy entry final RootEntitySourceImpl rootEntitySource = new RootEntitySourceImpl( mappingDocument, jaxbRootEntity ); entitySourceByNameMap.put( rootEntitySource.getEntityNamingSource().getEntityName(), rootEntitySource ); final EntityHierarchySourceImpl hierarchy = new EntityHierarchySourceImpl( rootEntitySource ); entityHierarchyList.add( hierarchy ); linkAnyWaiting( mappingDocument, rootEntitySource ); // process any of its nested sub-entity definitions processRootEntitySubEntityElements( mappingDocument, jaxbRootEntity, rootEntitySource ); } // iterate all discriminator-based subclass definitions at the hibernate-mapping level for ( JaxbHbmDiscriminatorSubclassEntityType discriminatorSubclassEntityBinding : mappingBinding.getSubclass() ) { processTopLevelSubClassBinding( mappingDocument, discriminatorSubclassEntityBinding ); } // iterate all joined-subclass definitions at the hibernate-mapping level for ( JaxbHbmJoinedSubclassEntityType joinedSubclassEntityBinding : mappingBinding.getJoinedSubclass() ) { processTopLevelSubClassBinding( mappingDocument, joinedSubclassEntityBinding ); } // iterate all union-subclass definitions at the hibernate-mapping level for ( JaxbHbmUnionSubclassEntityType unionSubclassEntityBinding : mappingBinding.getUnionSubclass() ) { processTopLevelSubClassBinding( mappingDocument, unionSubclassEntityBinding ); } } private void processRootEntitySubEntityElements( MappingDocument mappingDocument, JaxbHbmRootEntityType jaxbRootEntity, RootEntitySourceImpl rootEntitySource) { // todo : technically we should only allow one mutually-exclusive; should we enforce that here? // I believe the DTD/XSD does enforce that, so maybe not a big deal processElements( mappingDocument, jaxbRootEntity.getSubclass(), rootEntitySource ); processElements( mappingDocument, jaxbRootEntity.getJoinedSubclass(), rootEntitySource ); processElements( mappingDocument, jaxbRootEntity.getUnionSubclass(), rootEntitySource ); } private void processSubEntityElements( MappingDocument mappingDocument, JaxbHbmEntityBaseDefinition entityBinding, AbstractEntitySourceImpl container) { if ( JaxbHbmDiscriminatorSubclassEntityType.class.isInstance( entityBinding ) ) { final JaxbHbmDiscriminatorSubclassEntityType jaxbSubclass = (JaxbHbmDiscriminatorSubclassEntityType) entityBinding; processElements( mappingDocument, jaxbSubclass.getSubclass(), container ); } else if ( JaxbHbmJoinedSubclassEntityType.class.isInstance( entityBinding ) ) { final JaxbHbmJoinedSubclassEntityType jaxbJoinedSubclass = (JaxbHbmJoinedSubclassEntityType) entityBinding; processElements( mappingDocument, jaxbJoinedSubclass.getJoinedSubclass(), container ); } else if ( JaxbHbmUnionSubclassEntityType.class.isInstance( entityBinding ) ) { final JaxbHbmUnionSubclassEntityType jaxbUnionSubclass = (JaxbHbmUnionSubclassEntityType) entityBinding; processElements( mappingDocument, jaxbUnionSubclass.getUnionSubclass(), container ); } } private void processElements( MappingDocument mappingDocument, List nestedSubEntityList, AbstractEntitySourceImpl container) { for ( final JaxbHbmSubclassEntityBaseDefinition jaxbSubEntity : nestedSubEntityList ) { final SubclassEntitySourceImpl subClassEntitySource = createSubClassEntitySource( mappingDocument, jaxbSubEntity, container ); entitySourceByNameMap.put( subClassEntitySource.getEntityNamingSource().getEntityName(), subClassEntitySource ); container.add( subClassEntitySource ); linkAnyWaiting( mappingDocument, subClassEntitySource ); // Re-run the sub element to handle subclasses within the subclass. processSubEntityElements( mappingDocument, jaxbSubEntity, subClassEntitySource ); } } private SubclassEntitySourceImpl createSubClassEntitySource( MappingDocument mappingDocument, JaxbHbmSubclassEntityBaseDefinition jaxbSubEntity, EntitySource superEntity) { if ( JaxbHbmJoinedSubclassEntityType.class.isInstance( jaxbSubEntity ) ) { return new JoinedSubclassEntitySourceImpl( mappingDocument, JaxbHbmJoinedSubclassEntityType.class.cast( jaxbSubEntity ), superEntity ); } else { return new SubclassEntitySourceImpl( mappingDocument, jaxbSubEntity, superEntity ); } } private void processTopLevelSubClassBinding( MappingDocument mappingDocument, JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) { final AbstractEntitySourceImpl entityItExtends = locateExtendedEntitySource( mappingDocument, jaxbSubEntityMapping ); if ( entityItExtends == null ) { // we have not seen its declared super-type yet, add it to the queue to be linked up // later when (if) we do addToToBeLinkedQueue( mappingDocument, jaxbSubEntityMapping ); } else { // we have seen its super-type already final SubclassEntitySourceImpl subEntitySource = createSubClassEntitySource( mappingDocument, jaxbSubEntityMapping, entityItExtends ); entitySourceByNameMap.put( subEntitySource.getEntityNamingSource().getEntityName(), subEntitySource ); entityItExtends.add( subEntitySource ); // this may have been a "middle type". So link any sub entities that may be waiting on it linkAnyWaiting( mappingDocument, subEntitySource ); processSubEntityElements( mappingDocument, jaxbSubEntityMapping, subEntitySource ); } } private AbstractEntitySourceImpl locateExtendedEntitySource( MappingDocument mappingDocument, JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) { // NOTE : extends may refer to either an entity-name or a class-name, we need to check each // first check using entity-name AbstractEntitySourceImpl entityItExtends = entitySourceByNameMap.get( jaxbSubEntityMapping.getExtends() ); if ( entityItExtends == null ) { // next, check using class name entityItExtends = entitySourceByNameMap.get( mappingDocument.qualifyClassName( jaxbSubEntityMapping.getExtends() ) ); } return entityItExtends; } private void addToToBeLinkedQueue( MappingDocument mappingDocument, JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) { List waitingList = null; if ( toBeLinkedQueue == null ) { toBeLinkedQueue = new HashMap>(); } else { waitingList = toBeLinkedQueue.get( jaxbSubEntityMapping.getExtends() ); } if ( waitingList == null ) { waitingList = new ArrayList(); toBeLinkedQueue.put( jaxbSubEntityMapping.getExtends(), waitingList ); } waitingList.add( new ExtendsQueueEntry( mappingDocument, jaxbSubEntityMapping ) ); } private void linkAnyWaiting( MappingDocument mappingDocument, AbstractEntitySourceImpl entitySource) { if ( toBeLinkedQueue == null ) { return; } List waitingList = toBeLinkedQueue.remove( entitySource.jaxbEntityMapping().getEntityName() ); if ( waitingList != null ) { processWaitingSubEntityMappings( entitySource, waitingList ); waitingList.clear(); } if ( StringHelper.isNotEmpty( entitySource.jaxbEntityMapping().getName() ) ) { final String entityClassName = entitySource.jaxbEntityMapping().getName(); waitingList = toBeLinkedQueue.remove( entityClassName ); if ( waitingList != null ) { processWaitingSubEntityMappings( entitySource, waitingList ); waitingList.clear(); } final String qualifiedEntityClassName = mappingDocument.qualifyClassName( entityClassName ); if ( !entityClassName.equals( qualifiedEntityClassName ) ) { waitingList = toBeLinkedQueue.remove( qualifiedEntityClassName ); if ( waitingList != null ) { processWaitingSubEntityMappings( entitySource, waitingList ); waitingList.clear(); } } } } private void processWaitingSubEntityMappings( AbstractEntitySourceImpl entitySource, List waitingList) { for ( ExtendsQueueEntry entry : waitingList ) { final SubclassEntitySourceImpl subEntitySource = createSubClassEntitySource( entry.sourceMappingDocument, entry.jaxbSubEntityMapping, entitySource ); entitySourceByNameMap.put( subEntitySource.getEntityNamingSource().getEntityName(), subEntitySource ); entitySource.add( subEntitySource ); // this may have been a "middle type". So link any sub entities that may be waiting on it linkAnyWaiting( entry.sourceMappingDocument, subEntitySource ); processSubEntityElements( entry.sourceMappingDocument, entry.jaxbSubEntityMapping, subEntitySource ); } } private static class ExtendsQueueEntry { private final MappingDocument sourceMappingDocument; private final JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping; public ExtendsQueueEntry( MappingDocument sourceMappingDocument, JaxbHbmSubclassEntityBaseDefinition jaxbSubEntityMapping) { this.sourceMappingDocument = sourceMappingDocument; this.jaxbSubEntityMapping = jaxbSubEntityMapping; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy