/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, 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 org.dom4j.Attribute;
import org.hibernate.EntityMode;
import org.hibernate.MappingException;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.binding.AttributeBinding;
import org.hibernate.metamodel.binding.BagBinding;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.ManyToOneAttributeBinding;
import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.metamodel.binding.SimpleAttributeBinding;
import org.hibernate.metamodel.domain.Entity;
import org.hibernate.metamodel.domain.Hierarchical;
import org.hibernate.metamodel.domain.PluralAttributeNature;
import org.hibernate.metamodel.relational.Schema;
import org.hibernate.metamodel.relational.Table;
import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.relational.UniqueKey;
import org.hibernate.metamodel.source.hbm.state.domain.HbmManyToOneAttributeDomainState;
import org.hibernate.metamodel.source.hbm.state.relational.HbmManyToOneRelationalStateContainer;
import org.hibernate.metamodel.source.hbm.xml.mapping.*;
import org.hibernate.metamodel.source.internal.MetadataImpl;
import org.hibernate.metamodel.source.hbm.state.domain.HbmPluralAttributeDomainState;
import org.hibernate.metamodel.source.hbm.state.domain.HbmSimpleAttributeDomainState;
import org.hibernate.metamodel.source.hbm.state.relational.HbmSimpleValueRelationalStateContainer;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
abstract class AbstractEntityBinder {
private final HibernateMappingBinder hibernateMappingBinder;
private final Schema.Name schemaName;
AbstractEntityBinder(HibernateMappingBinder hibernateMappingBinder,
XMLHibernateMapping.XMLClass entityClazz) {
this.hibernateMappingBinder = hibernateMappingBinder;
this.schemaName = new Schema.Name(
( entityClazz.getSchema() == null ?
hibernateMappingBinder.getDefaultSchemaName() :
entityClazz.getSchema() ),
( entityClazz.getCatalog() == null ?
hibernateMappingBinder.getDefaultCatalogName() :
entityClazz.getCatalog() )
);
}
protected HibernateMappingBinder getHibernateMappingBinder() {
return hibernateMappingBinder;
}
protected HibernateXmlBinder getHibernateXmlBinder() {
return hibernateMappingBinder.getHibernateXmlBinder();
}
protected MetadataImpl getMetadata() {
return hibernateMappingBinder.getHibernateXmlBinder().getMetadata();
}
protected Schema.Name getSchemaName() {
return schemaName;
}
protected NamingStrategy getNamingStrategy() {
return getMetadata().getNamingStrategy();
}
protected void basicEntityBinding(XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding,
Hierarchical superType) {
entityBinding.fromHbmXml(
hibernateMappingBinder,
entityClazz,
new Entity( hibernateMappingBinder.extractEntityName( entityClazz ), superType )
);
// TODO: move this stuff out
// transfer an explicitly defined lazy attribute
bindPojoRepresentation( entityClazz, entityBinding );
bindDom4jRepresentation( entityClazz, entityBinding );
bindMapRepresentation( entityClazz, entityBinding );
final String entityName = entityBinding.getEntity().getName();
if ( entityClazz.getFetchProfile() != null ) {
hibernateMappingBinder.parseFetchProfiles( entityClazz.getFetchProfile(), entityName );
}
getMetadata().addImport( entityName, entityName );
if ( hibernateMappingBinder.isAutoImport() ) {
if ( entityName.indexOf( '.' ) > 0 ) {
getMetadata().addImport( StringHelper.unqualify( entityName ), entityName );
}
}
}
protected String getDefaultAccess() {
return hibernateMappingBinder.getDefaultAccess();
}
private void bindPojoRepresentation(XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding) {
String className = hibernateMappingBinder.getClassName( entityClazz.getName() );
String proxyName = hibernateMappingBinder.getClassName( entityClazz.getProxy() );
entityBinding.getEntity().getPojoEntitySpecifics().setClassName( className );
if ( proxyName != null ) {
entityBinding.getEntity().getPojoEntitySpecifics().setProxyInterfaceName( proxyName );
entityBinding.setLazy( true );
}
else if ( entityBinding.isLazy() ) {
entityBinding.getEntity().getPojoEntitySpecifics().setProxyInterfaceName( className );
}
XMLTuplizerElement tuplizer = locateTuplizerDefinition( entityClazz, EntityMode.POJO );
if ( tuplizer != null ) {
entityBinding.getEntity().getPojoEntitySpecifics().setTuplizerClassName( tuplizer.getClazz() );
}
}
private void bindDom4jRepresentation(XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding) {
String nodeName = entityClazz.getNode();
if ( nodeName == null ) {
nodeName = StringHelper.unqualify( entityBinding.getEntity().getName() );
}
entityBinding.getEntity().getDom4jEntitySpecifics().setNodeName(nodeName);
XMLTuplizerElement tuplizer = locateTuplizerDefinition( entityClazz, EntityMode.DOM4J );
if ( tuplizer != null ) {
entityBinding.getEntity().getDom4jEntitySpecifics().setTuplizerClassName( tuplizer.getClazz() );
}
}
private void bindMapRepresentation(XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding) {
XMLTuplizerElement tuplizer = locateTuplizerDefinition( entityClazz, EntityMode.MAP );
if ( tuplizer != null ) {
entityBinding.getEntity().getMapEntitySpecifics().setTuplizerClassName( tuplizer.getClazz() );
}
}
/**
* Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
*
* @param container The containing element (representing the entity/component)
* @param entityMode The entity-mode for which to locate the tuplizer element
*
* @return The tuplizer element, or null.
*/
private static XMLTuplizerElement locateTuplizerDefinition(XMLHibernateMapping.XMLClass container,
EntityMode entityMode) {
for ( XMLTuplizerElement tuplizer : container.getTuplizer() ) {
if ( entityMode.toString().equals( tuplizer.getEntityMode() ) ) {
return tuplizer;
}
}
return null;
}
int getOptimisticLockMode(Attribute olAtt) throws MappingException {
if ( olAtt == null ) {
return Versioning.OPTIMISTIC_LOCK_VERSION;
}
String olMode = olAtt.getValue();
if ( olMode == null || "version".equals( olMode ) ) {
return Versioning.OPTIMISTIC_LOCK_VERSION;
}
else if ( "dirty".equals( olMode ) ) {
return Versioning.OPTIMISTIC_LOCK_DIRTY;
}
else if ( "all".equals( olMode ) ) {
return Versioning.OPTIMISTIC_LOCK_ALL;
}
else if ( "none".equals( olMode ) ) {
return Versioning.OPTIMISTIC_LOCK_NONE;
}
else {
throw new MappingException( "Unsupported optimistic-lock style: " + olMode );
}
}
protected String getClassTableName(
XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding,
Table denormalizedSuperTable) {
final String entityName = entityBinding.getEntity().getName();
String logicalTableName;
String physicalTableName;
if ( entityClazz.getTable() == null ) {
logicalTableName = StringHelper.unqualify( entityName );
physicalTableName = getHibernateXmlBinder().getMetadata().getNamingStrategy().classToTableName( entityName );
}
else {
logicalTableName = entityClazz.getTable();
physicalTableName = getHibernateXmlBinder().getMetadata().getNamingStrategy().tableName( logicalTableName );
}
// todo : find out the purpose of these logical bindings
// mappings.addTableBinding( schema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable );
return physicalTableName;
}
protected void buildAttributeBindings(XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding) {
// null = UniqueKey (we are not binding a natural-id mapping)
// true = mutable, by default properties are mutable
// true = nullable, by default properties are nullable.
buildAttributeBindings( entityClazz, entityBinding, null, true, true );
}
/**
* This form is essentially used to create natural-id mappings. But the processing is the same, aside from these
* extra parameterized values, so we encapsulate it here.
*
* @param entityClazz
* @param entityBinding
* @param uniqueKey
* @param mutable
* @param nullable
*/
protected void buildAttributeBindings(
XMLHibernateMapping.XMLClass entityClazz,
EntityBinding entityBinding,
UniqueKey uniqueKey,
boolean mutable,
boolean nullable) {
final boolean naturalId = uniqueKey != null;
final String entiytName = entityBinding.getEntity().getName();
final TableSpecification tabe = entityBinding.getBaseTable();
AttributeBinding attributeBinding = null;
for ( Object attribute : entityClazz.getPropertyOrManyToOneOrOneToOne() ) {
if ( XMLBagElement.class.isInstance( attribute ) ) {
XMLBagElement collection = XMLBagElement.class.cast( attribute );
BagBinding collectionBinding = entityBinding.makeBagAttributeBinding( collection.getName() );
bindBag( collection, collectionBinding, entityBinding );
hibernateMappingBinder.getHibernateXmlBinder().getMetadata().addCollection( collectionBinding );
attributeBinding = collectionBinding;
}
else if ( XMLIdbagElement.class.isInstance( attribute ) ) {
XMLIdbagElement collection = XMLIdbagElement.class.cast( attribute );
//BagBinding collectionBinding = entityBinding.makeBagAttributeBinding( collection.getName() );
//bindIdbag( collection, bagBinding, entityBinding, PluralAttributeNature.BAG, collection.getName() );
// todo: handle identifier
//attributeBinding = collectionBinding;
//hibernateMappingBinder.getHibernateXmlBinder().getMetadata().addCollection( attributeBinding );
}
else if ( XMLSetElement.class.isInstance( attribute ) ) {
XMLSetElement collection = XMLSetElement.class.cast( attribute );
//BagBinding collectionBinding = entityBinding.makeBagAttributeBinding( collection.getName() );
//bindSet( collection, collectionBinding, entityBinding, PluralAttributeNature.SET, collection.getName() );
//attributeBinding = collectionBinding;
//hibernateMappingBinder.getHibernateXmlBinder().getMetadata().addCollection( attributeBinding );
}
else if ( XMLListElement.class.isInstance( attribute ) ) {
XMLListElement collection = XMLListElement.class.cast( attribute );
//ListBinding collectionBinding = entityBinding.makeBagAttributeBinding( collection.getName() );
//bindList( collection, bagBinding, entityBinding, PluralAttributeNature.LIST, collection.getName() );
// todo : handle list index
//attributeBinding = collectionBinding;
//hibernateMappingBinder.getHibernateXmlBinder().getMetadata().addCollection( attributeBinding );
}
else if ( XMLMapElement.class.isInstance( attribute ) ) {
XMLMapElement collection = XMLMapElement.class.cast( attribute );
//BagBinding bagBinding = entityBinding.makeBagAttributeBinding( collection.getName() );
//bindMap( collection, bagBinding, entityBinding, PluralAttributeNature.MAP, collection.getName() );
// todo : handle map key
//hibernateMappingBinder.getHibernateXmlBinder().getMetadata().addCollection( attributeBinding );
}
else if ( XMLManyToOneElement.class.isInstance( attribute ) ) {
XMLManyToOneElement manyToOne = XMLManyToOneElement.class.cast( attribute );
ManyToOneAttributeBinding manyToOneBinding = entityBinding.makeManyToOneAttributeBinding( manyToOne.getName() );
bindManyToOne( manyToOne, manyToOneBinding, entityBinding );
attributeBinding = manyToOneBinding;
// todo : implement
// value = new ManyToOne( mappings, table );
// bindManyToOne( subElement, (ManyToOne) value, propertyName, nullable, mappings );
}
else if ( XMLAnyElement.class.isInstance( attribute ) ) {
// todo : implement
// value = new Any( mappings, table );
// bindAny( subElement, (Any) value, nullable, mappings );
}
else if ( XMLOneToOneElement.class.isInstance( attribute ) ) {
// todo : implement
// value = new OneToOne( mappings, table, persistentClass );
// bindOneToOne( subElement, (OneToOne) value, propertyName, true, mappings );
}
else if ( XMLPropertyElement.class.isInstance( attribute ) ) {
XMLPropertyElement property = XMLPropertyElement.class.cast( attribute );
SimpleAttributeBinding binding = entityBinding.makeSimpleAttributeBinding( property.getName() );
bindSimpleAttribute( property, binding, entityBinding );
attributeBinding = binding;
}
else if ( XMLComponentElement.class.isInstance( attribute )
|| XMLDynamicComponentElement.class.isInstance( attribute )
|| XMLPropertiesElement.class.isInstance( attribute ) ) {
// todo : implement
// String subpath = StringHelper.qualify( entityName, propertyName );
// value = new Component( mappings, persistentClass );
//
// bindComponent(
// subElement,
// (Component) value,
// persistentClass.getClassName(),
// propertyName,
// subpath,
// true,
// "properties".equals( subElementName ),
// mappings,
// inheritedMetas,
// false
// );
}
}
/*
Array
PrimitiveArray
*/
for ( XMLJoinElement join : entityClazz.getJoin() ) {
// todo : implement
// Join join = new Join();
// join.setPersistentClass( persistentClass );
// bindJoin( subElement, join, mappings, inheritedMetas );
// persistentClass.addJoin( join );
}
for ( XMLSubclassElement subclass : entityClazz.getSubclass() ) {
// todo : implement
// handleSubclass( persistentClass, mappings, subElement, inheritedMetas );
}
for ( XMLJoinedSubclassElement subclass : entityClazz.getJoinedSubclass() ) {
// todo : implement
// handleJoinedSubclass( persistentClass, mappings, subElement, inheritedMetas );
}
for ( XMLUnionSubclassElement subclass : entityClazz.getUnionSubclass() ) {
// todo : implement
// handleUnionSubclass( persistentClass, mappings, subElement, inheritedMetas );
}
for ( XMLFilterElement filter : entityClazz.getFilter() ) {
// todo : implement
// parseFilter( subElement, entityBinding );
}
if ( entityClazz.getNaturalId() != null ) {
// todo : implement
// UniqueKey uk = new UniqueKey();
// uk.setName("_UniqueKey");
// uk.setTable(table);
// //by default, natural-ids are "immutable" (constant)
// boolean mutableId = "true".equals( subElement.attributeValue("mutable") );
// createClassProperties(
// subElement,
// persistentClass,
// mappings,
// inheritedMetas,
// uk,
// mutableId,
// false,
// true
// );
// table.addUniqueKey(uk);
}
if ( entityClazz.getQueryOrSqlQuery() != null ) {
for ( Object queryOrSqlQuery : entityClazz.getQueryOrSqlQuery() ) {
if ( XMLQueryElement.class.isInstance( queryOrSqlQuery ) ) {
// todo : implement
// bindNamedQuery(subElement, persistentClass.getEntityName(), mappings);
}
else if ( XMLSqlQueryElement.class.isInstance( queryOrSqlQuery ) ) {
// todo : implement
// bindNamedSQLQuery(subElement, persistentClass.getEntityName(), mappings);
}
}
}
if ( entityClazz.getResultset() != null ) {
for ( XMLResultsetElement resultSet : entityClazz.getResultset() ) {
// todo : implement
// bindResultSetMappingDefinition( subElement, persistentClass.getEntityName(), mappings );
}
}
// if ( value != null ) {
// Property property = createProperty( value, propertyName, persistentClass
// .getClassName(), subElement, mappings, inheritedMetas );
// if ( !mutable ) property.setUpdateable(false);
// if ( naturalId ) property.setNaturalIdentifier(true);
// persistentClass.addProperty( property );
// if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
// }
}
protected void bindSimpleAttribute(XMLHibernateMapping.XMLClass.XMLId id,
SimpleAttributeBinding attributeBinding,
EntityBinding entityBinding,
String attributeName) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmSimpleAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( attributeName ),
entityBinding.getMetaAttributes(),
id
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initializeSimpleTupleValue(
new HbmSimpleValueRelationalStateContainer(
getHibernateMappingBinder(),
true,
id
)
);
}
}
protected void bindSimpleAttribute(XMLHibernateMapping.XMLClass.XMLDiscriminator discriminator,
SimpleAttributeBinding attributeBinding,
EntityBinding entityBinding,
String attributeName) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmSimpleAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( attributeName ),
entityBinding.getMetaAttributes(),
discriminator
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initializeSimpleTupleValue(
new HbmSimpleValueRelationalStateContainer(
getHibernateMappingBinder(),
true,
discriminator
)
);
}
}
protected void bindSimpleAttribute(XMLHibernateMapping.XMLClass.XMLVersion version,
SimpleAttributeBinding attributeBinding,
EntityBinding entityBinding,
String attributeName) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmSimpleAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( attributeName ),
entityBinding.getMetaAttributes(),
version
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initializeSimpleTupleValue(
new HbmSimpleValueRelationalStateContainer(
getHibernateMappingBinder(),
true,
version
)
);
}
}
protected void bindSimpleAttribute(XMLHibernateMapping.XMLClass.XMLTimestamp timestamp,
SimpleAttributeBinding attributeBinding,
EntityBinding entityBinding,
String attributeName) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmSimpleAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( attributeName ),
entityBinding.getMetaAttributes(),
timestamp
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initializeSimpleTupleValue(
new HbmSimpleValueRelationalStateContainer(
getHibernateMappingBinder(),
true,
timestamp
)
);
}
}
protected void bindSimpleAttribute(XMLPropertyElement property,
SimpleAttributeBinding attributeBinding,
EntityBinding entityBinding) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmSimpleAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( property.getName() ),
entityBinding.getMetaAttributes(),
property
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initializeSimpleTupleValue(
new HbmSimpleValueRelationalStateContainer(
getHibernateMappingBinder(),
true,
property
)
);
}
}
protected void bindBag(
XMLBagElement collection,
PluralAttributeBinding collectionBinding,
EntityBinding entityBinding) {
if ( collectionBinding.getAttribute() == null ) {
// domain model has not been bound yet
collectionBinding.initialize(
new HbmPluralAttributeDomainState(
hibernateMappingBinder,
collection,
entityBinding.getMetaAttributes(),
entityBinding.getEntity().getOrCreatePluralAttribute(
collection.getName(),
PluralAttributeNature.BAG
)
)
);
}
if ( collectionBinding.getValue() == null ) {
// todo : relational model binding
}
}
private void bindManyToOne(XMLManyToOneElement manyToOne,
ManyToOneAttributeBinding attributeBinding,
EntityBinding entityBinding) {
if ( attributeBinding.getAttribute() == null ) {
attributeBinding.initialize(
new HbmManyToOneAttributeDomainState(
hibernateMappingBinder,
entityBinding.getEntity().getOrCreateSingularAttribute( manyToOne.getName() ),
entityBinding.getMetaAttributes(),
manyToOne
)
);
}
if ( attributeBinding.getValue() == null ) {
// relational model has not been bound yet
// boolean (true here) indicates that by default column names should be guessed
attributeBinding.initialize(
new HbmManyToOneRelationalStateContainer(
getHibernateMappingBinder(),
true,
manyToOne
)
);
}
}
// private static Property createProperty(
// final Value value,
// final String propertyName,
// final String className,
// final Element subnode,
// final Mappings mappings,
// java.util.Map inheritedMetas) throws MappingException {
//
// if ( StringHelper.isEmpty( propertyName ) ) {
// throw new MappingException( subnode.getName() + " mapping must defined a name attribute [" + className + "]" );
// }
//
// value.setTypeUsingReflection( className, propertyName );
//
// // this is done here 'cos we might only know the type here (ugly!)
// // TODO: improve this a lot:
// if ( value instanceof ToOne ) {
// ToOne toOne = (ToOne) value;
// String propertyRef = toOne.getReferencedPropertyName();
// if ( propertyRef != null ) {
// mappings.addUniquePropertyReference( toOne.getReferencedEntityName(), propertyRef );
// }
// }
// else if ( value instanceof Collection ) {
// Collection coll = (Collection) value;
// String propertyRef = coll.getReferencedPropertyName();
// // not necessarily a *unique* property reference
// if ( propertyRef != null ) {
// mappings.addPropertyReference( coll.getOwnerEntityName(), propertyRef );
// }
// }
//
// value.createForeignKey();
// Property prop = new Property();
// prop.setValue( value );
// bindProperty( subnode, prop, mappings, inheritedMetas );
// return prop;
// }
// protected HbmRelationalState processValues(Element identifierElement, TableSpecification baseTable, String propertyPath, boolean isSimplePrimaryKey) {
// first boolean (false here) indicates that by default columns are nullable
// second boolean (true here) indicates that by default column names should be guessed
// todo : logical 1-1 handling
// final Attribute uniqueAttribute = node.attribute( "unique" );
// if ( uniqueAttribute != null
// && "true".equals( uniqueAttribute.getValue() )
// && ManyToOne.class.isInstance( simpleValue ) ) {
// ( (ManyToOne) simpleValue ).markAsLogicalOneToOne();
// }
//return processValues( identifierElement, baseTable, false, true, propertyPath, isSimplePrimaryKey );
}