/* * 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.internal; import java.util.Properties; import org.hibernate.AssertionFailure; import org.hibernate.metamodel.binding.AbstractCollectionElement; import org.hibernate.metamodel.binding.AbstractPluralAttributeBinding; import org.hibernate.metamodel.binding.AttributeBinding; import org.hibernate.metamodel.binding.BasicCollectionElement; import org.hibernate.metamodel.binding.CollectionElementNature; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityDiscriminator; import org.hibernate.metamodel.binding.HibernateTypeDescriptor; import org.hibernate.metamodel.binding.SingularAttributeBinding; import org.hibernate.metamodel.domain.SingularAttribute; import org.hibernate.metamodel.relational.Datatype; import org.hibernate.metamodel.relational.SimpleValue; import org.hibernate.metamodel.relational.Value; import org.hibernate.metamodel.source.MetadataImplementor; import org.hibernate.type.Type; import org.hibernate.type.TypeFactory; /** * This is a TEMPORARY way to initialize Hibernate types. * This class will be removed when types are resolved properly. * * @author Gail Badner */ class HibernateTypeResolver { private final MetadataImplementor metadata; HibernateTypeResolver(MetadataImplementor metadata) { this.metadata = metadata; } void resolve() { for ( EntityBinding entityBinding : metadata.getEntityBindings() ) { if ( entityBinding.getHierarchyDetails().getEntityDiscriminator() != null ) { resolveDiscriminatorTypeInformation( entityBinding.getHierarchyDetails().getEntityDiscriminator() ); } for ( AttributeBinding attributeBinding : entityBinding.attributeBindings() ) { if ( SingularAttributeBinding.class.isInstance( attributeBinding ) ) { resolveSingularAttributeTypeInformation( SingularAttributeBinding.class.cast( attributeBinding ) ); } else if ( AbstractPluralAttributeBinding.class.isInstance( attributeBinding ) ) { resolvePluralAttributeTypeInformation( AbstractPluralAttributeBinding.class.cast( attributeBinding ) ); } else { throw new AssertionFailure( "Unknown type of AttributeBinding: " + attributeBinding.getClass().getName() ); } } } } // perform any needed type resolutions for discriminator private void resolveDiscriminatorTypeInformation(EntityDiscriminator discriminator) { // perform any needed type resolutions for discriminator Type resolvedHibernateType = determineSingularTypeFromDescriptor( discriminator.getExplicitHibernateTypeDescriptor() ); if ( resolvedHibernateType != null ) { pushHibernateTypeInformationDownIfNeeded( discriminator.getExplicitHibernateTypeDescriptor(), discriminator.getBoundValue(), resolvedHibernateType ); } } private Type determineSingularTypeFromDescriptor(HibernateTypeDescriptor hibernateTypeDescriptor) { if ( hibernateTypeDescriptor.getResolvedTypeMapping() != null ) { return hibernateTypeDescriptor.getResolvedTypeMapping(); } String typeName = determineTypeName( hibernateTypeDescriptor ); Properties typeParameters = getTypeParameters( hibernateTypeDescriptor ); return getHeuristicType( typeName, typeParameters ); } private static String determineTypeName(HibernateTypeDescriptor hibernateTypeDescriptor) { return hibernateTypeDescriptor.getExplicitTypeName() != null ? hibernateTypeDescriptor.getExplicitTypeName() : hibernateTypeDescriptor.getJavaTypeName(); } private static Properties getTypeParameters(HibernateTypeDescriptor hibernateTypeDescriptor) { Properties typeParameters = new Properties( ); if ( hibernateTypeDescriptor.getTypeParameters() != null ) { typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() ); } return typeParameters; } // perform any needed type resolutions for SingularAttributeBinding private void resolveSingularAttributeTypeInformation(SingularAttributeBinding attributeBinding) { if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() != null ) { return; } // we can determine the Hibernate Type if either: // 1) the user explicitly named a Type in a HibernateTypeDescriptor // 2) we know the java type of the attribute Type resolvedType; resolvedType = determineSingularTypeFromDescriptor( attributeBinding.getHibernateTypeDescriptor() ); if ( resolvedType == null ) { if ( ! attributeBinding.getAttribute().isSingular() ) { throw new AssertionFailure( "SingularAttributeBinding object has a plural attribute: " + attributeBinding.getAttribute().getName() ); } final SingularAttribute singularAttribute = ( SingularAttribute ) attributeBinding.getAttribute(); if ( singularAttribute.getSingularAttributeType() != null ) { resolvedType = getHeuristicType( singularAttribute.getSingularAttributeType().getClassName(), new Properties() ); } } if ( resolvedType != null ) { pushHibernateTypeInformationDownIfNeeded( attributeBinding, resolvedType ); } } // perform any needed type resolutions for PluralAttributeBinding private void resolvePluralAttributeTypeInformation(AbstractPluralAttributeBinding attributeBinding) { if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() != null ) { return; } Type resolvedType; // do NOT look at java type... //String typeName = determineTypeName( attributeBinding.getHibernateTypeDescriptor() ); String typeName = attributeBinding.getHibernateTypeDescriptor().getExplicitTypeName(); if ( typeName != null ) { resolvedType = metadata.getTypeResolver() .getTypeFactory() .customCollection( typeName, getTypeParameters( attributeBinding.getHibernateTypeDescriptor() ), attributeBinding.getAttribute().getName(), attributeBinding.getReferencedPropertyName(), attributeBinding.getCollectionElement().getCollectionElementNature() == CollectionElementNature.COMPOSITE ); } else { resolvedType = determineDefaultCollectionInformation( attributeBinding ); } if ( resolvedType != null ) { pushHibernateTypeInformationDownIfNeeded( attributeBinding.getHibernateTypeDescriptor(), null, resolvedType ); } resolveCollectionElementTypeInformation( attributeBinding.getCollectionElement() ); } private Type determineDefaultCollectionInformation(AbstractPluralAttributeBinding attributeBinding) { final TypeFactory typeFactory = metadata.getTypeResolver().getTypeFactory(); switch ( attributeBinding.getAttribute().getNature() ) { case SET: { return typeFactory.set( attributeBinding.getAttribute().getName(), attributeBinding.getReferencedPropertyName(), attributeBinding.getCollectionElement().getCollectionElementNature() == CollectionElementNature.COMPOSITE ); } case BAG: { return typeFactory.bag( attributeBinding.getAttribute().getName(), attributeBinding.getReferencedPropertyName(), attributeBinding.getCollectionElement() .getCollectionElementNature() == CollectionElementNature.COMPOSITE ); } default: { throw new UnsupportedOperationException( "Collection type not supported yet:" + attributeBinding.getAttribute().getNature() ); } } } private void resolveCollectionElementTypeInformation(AbstractCollectionElement collectionElement) { switch ( collectionElement.getCollectionElementNature() ) { case BASIC: { resolveBasicCollectionElement( BasicCollectionElement.class.cast( collectionElement ) ); break; } case COMPOSITE: case ONE_TO_MANY: case MANY_TO_MANY: case MANY_TO_ANY: { throw new UnsupportedOperationException( "Collection element nature not supported yet: " + collectionElement.getCollectionElementNature() ); } default: { throw new AssertionFailure( "Unknown collection element nature : " + collectionElement.getCollectionElementNature() ); } } } private void resolveBasicCollectionElement(BasicCollectionElement basicCollectionElement) { Type resolvedHibernateType = determineSingularTypeFromDescriptor( basicCollectionElement.getHibernateTypeDescriptor() ); if ( resolvedHibernateType != null ) { pushHibernateTypeInformationDownIfNeeded( basicCollectionElement.getHibernateTypeDescriptor(), basicCollectionElement.getElementValue(), resolvedHibernateType ); } } private Type getHeuristicType(String typeName, Properties typeParameters) { if ( typeName != null ) { try { return metadata.getTypeResolver().heuristicType( typeName, typeParameters ); } catch (Exception ignore) { } } return null; } private void pushHibernateTypeInformationDownIfNeeded(SingularAttributeBinding attributeBinding, Type resolvedHibernateType) { final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor(); final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() ); final Value value = attributeBinding.getValue(); if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) { singularAttribute.resolveType( metadata.makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) ); } // sql type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this.pushHibernateTypeInformationDownIfNeeded( hibernateTypeDescriptor, value, resolvedHibernateType ); } private void pushHibernateTypeInformationDownIfNeeded( HibernateTypeDescriptor hibernateTypeDescriptor, Value value, Type resolvedHibernateType) { if ( resolvedHibernateType == null ) { return; } if ( hibernateTypeDescriptor.getResolvedTypeMapping() == null ) { hibernateTypeDescriptor.setResolvedTypeMapping( resolvedHibernateType ); } // java type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( hibernateTypeDescriptor.getJavaTypeName() == null ) { hibernateTypeDescriptor.setJavaTypeName( resolvedHibernateType.getReturnedClass().getName() ); } // todo : this can be made a lot smarter, but for now this will suffice. currently we only handle single value bindings if ( SimpleValue.class.isInstance( value ) ) { SimpleValue simpleValue = ( SimpleValue ) value; if ( simpleValue.getDatatype() == null ) { simpleValue.setDatatype( new Datatype( resolvedHibernateType.sqlTypes( metadata )[0], resolvedHibernateType.getName(), resolvedHibernateType.getReturnedClass() ) ); } } } }