/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.metamodel.source.hbm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.metamodel.source.binder.SubclassEntityContainer;
import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.EntityElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.SubEntityElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLHibernateMapping;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLJoinedSubclassElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLSubclassElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLUnionSubclassElement;
/**
* @author Steve Ebersole
*/
public class HierarchyBuilder {
private final List<EntityHierarchyImpl> entityHierarchies = new ArrayList<EntityHierarchyImpl>();
// process state
private final Map<String,SubclassEntityContainer> subEntityContainerMap = new HashMap<String, SubclassEntityContainer>();
private final List<ExtendsQueueEntry> extendsQueue = new ArrayList<ExtendsQueueEntry>();
// mapping file specific state
private MappingDocument currentMappingDocument;
public void processMappingDocument(MappingDocument mappingDocument) {
this.currentMappingDocument = mappingDocument;
try {
processCurrentMappingDocument();
}
finally {
this.currentMappingDocument = null;
}
}
private void processCurrentMappingDocument() {
for ( Object entityElementO : currentMappingDocument.getMappingRoot().getClazzOrSubclassOrJoinedSubclass() ) {
final EntityElement entityElement = (EntityElement) entityElementO;
if ( XMLHibernateMapping.XMLClass.class.isInstance( entityElement ) ) {
// we can immediately handle <class/> elements in terms of creating the hierarchy entry
final XMLHibernateMapping.XMLClass xmlClass = (XMLHibernateMapping.XMLClass) entityElement;
final RootEntitySourceImpl rootEntitySource = new RootEntitySourceImpl( currentMappingDocument, xmlClass );
final EntityHierarchyImpl hierarchy = new EntityHierarchyImpl( rootEntitySource );
entityHierarchies.add( hierarchy );
subEntityContainerMap.put( rootEntitySource.getEntityName(), rootEntitySource );
processSubElements( entityElement, rootEntitySource );
}
else {
// we have to see if this things super-type has been found yet, and if not add it to the
// extends queue
final SubclassEntitySourceImpl subClassEntitySource = new SubclassEntitySourceImpl( currentMappingDocument, entityElement );
final String entityName = subClassEntitySource.getEntityName();
subEntityContainerMap.put( entityName, subClassEntitySource );
final String entityItExtends = currentMappingDocument.getMappingLocalBindingContext().qualifyClassName(
((SubEntityElement) entityElement).getExtends()
);
processSubElements( entityElement, subClassEntitySource );
final SubclassEntityContainer container = subEntityContainerMap.get( entityItExtends );
if ( container != null ) {
// we already have this entity's super, attach it and continue
container.add( subClassEntitySource );
}
else {
// we do not yet have the super and have to wait, so add it fto the extends queue
extendsQueue.add( new ExtendsQueueEntry( subClassEntitySource, entityItExtends ) );
}
}
}
}
public List<EntityHierarchyImpl> groupEntityHierarchies() {
while ( ! extendsQueue.isEmpty() ) {
// set up a pass over the queue
int numberOfMappingsProcessed = 0;
Iterator<ExtendsQueueEntry> iterator = extendsQueue.iterator();
while ( iterator.hasNext() ) {
final ExtendsQueueEntry entry = iterator.next();
final SubclassEntityContainer container = subEntityContainerMap.get( entry.entityItExtends );
if ( container != null ) {
// we now have this entity's super, attach it and remove entry from extends queue
container.add( entry.subClassEntitySource );
iterator.remove();
numberOfMappingsProcessed++;
}
}
if ( numberOfMappingsProcessed == 0 ) {
// todo : we could log the waiting dependencies...
throw new MappingException( "Unable to process extends dependencies in hbm files" );
}
}
return entityHierarchies;
}
private void processSubElements(EntityElement entityElement, SubclassEntityContainer container) {
if ( XMLHibernateMapping.XMLClass.class.isInstance( entityElement ) ) {
final XMLHibernateMapping.XMLClass xmlClass = (XMLHibernateMapping.XMLClass) entityElement;
processElements( xmlClass.getJoinedSubclass(), container );
processElements( xmlClass.getSubclass(), container );
processElements( xmlClass.getUnionSubclass(), container );
}
else if ( XMLSubclassElement.class.isInstance( entityElement ) ) {
final XMLSubclassElement xmlSubclass = (XMLSubclassElement) entityElement;
processElements( xmlSubclass.getSubclass(), container );
}
else if ( XMLJoinedSubclassElement.class.isInstance( entityElement ) ) {
final XMLJoinedSubclassElement xmlJoinedSubclass = (XMLJoinedSubclassElement) entityElement;
processElements( xmlJoinedSubclass.getJoinedSubclass(), container );
}
else if ( XMLUnionSubclassElement.class.isInstance( entityElement ) ) {
final XMLUnionSubclassElement xmlUnionSubclass = (XMLUnionSubclassElement) entityElement;
processElements( xmlUnionSubclass.getUnionSubclass(), container );
}
}
private void processElements(List subElements, SubclassEntityContainer container) {
for ( Object subElementO : subElements ) {
final SubEntityElement subElement = (SubEntityElement) subElementO;
final SubclassEntitySourceImpl subclassEntitySource = new SubclassEntitySourceImpl( currentMappingDocument, subElement );
container.add( subclassEntitySource );
final String subEntityName = subclassEntitySource.getEntityName();
subEntityContainerMap.put( subEntityName, subclassEntitySource );
}
}
private static class ExtendsQueueEntry {
private final SubclassEntitySource subClassEntitySource;
private final String entityItExtends;
private ExtendsQueueEntry(SubclassEntitySource subClassEntitySource, String entityItExtends) {
this.subClassEntitySource = subClassEntitySource;
this.entityItExtends = entityItExtends;
}
}
}