Open Source Repository

Home /hibernate/hibernate-3.2.6.ga | Repository Home



org/hibernate/type/TypeFactory.java
// $Id: TypeFactory.java 12936 2007-08-14 19:45:56Z gbadner $
package org.hibernate.type;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;

import org.hibernate.Hibernate;
import org.hibernate.MappingException;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.intercept.LazyPropertyInitializer;
import org.hibernate.property.BackrefPropertyAccessor;
import org.hibernate.tuple.StandardProperty;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.util.ReflectHelper;

/**
 * Used internally to obtain instances of <tt>Type</tt>. Applications should use static methods
 * and constants on <tt>org.hibernate.Hibernate</tt>.
 
 @see org.hibernate.Hibernate
 @author Gavin King
 */
public final class TypeFactory {

  private static final Map BASIC_TYPES;

  static {
    HashMap basics = new HashMap();
    basics.putboolean.class.getName(), Hibernate.BOOLEAN );
    basics.putlong.class.getName(), Hibernate.LONG );
    basics.putshort.class.getName(), Hibernate.SHORT );
    basics.putint.class.getName(), Hibernate.INTEGER );
    basics.putbyte.class.getName(), Hibernate.BYTE );
    basics.putfloat.class.getName(), Hibernate.FLOAT );
    basics.putdouble.class.getName(), Hibernate.DOUBLE );
    basics.putchar.class.getName(), Hibernate.CHARACTER );
    basics.putHibernate.CHARACTER.getName(), Hibernate.CHARACTER );
    basics.putHibernate.INTEGER.getName(), Hibernate.INTEGER );
    basics.putHibernate.STRING.getName(), Hibernate.STRING );
    basics.putHibernate.DATE.getName(), Hibernate.DATE );
    basics.putHibernate.TIME.getName(), Hibernate.TIME );
    basics.putHibernate.TIMESTAMP.getName(), Hibernate.TIMESTAMP );
    basics.put"dbtimestamp"new DbTimestampType() );
    basics.putHibernate.LOCALE.getName(), Hibernate.LOCALE );
    basics.putHibernate.CALENDAR.getName(), Hibernate.CALENDAR );
    basics.putHibernate.CALENDAR_DATE.getName(), Hibernate.CALENDAR_DATE );
    basics.putHibernate.CURRENCY.getName(), Hibernate.CURRENCY );
    basics.putHibernate.TIMEZONE.getName(), Hibernate.TIMEZONE );
    basics.putHibernate.CLASS.getName(), Hibernate.CLASS );
    basics.putHibernate.TRUE_FALSE.getName(), Hibernate.TRUE_FALSE );
    basics.putHibernate.YES_NO.getName(), Hibernate.YES_NO );
    basics.putHibernate.BINARY.getName(), Hibernate.BINARY );
    basics.putHibernate.TEXT.getName(), Hibernate.TEXT );
    basics.putHibernate.BLOB.getName(), Hibernate.BLOB );
    basics.putHibernate.CLOB.getName(), Hibernate.CLOB );
    basics.putHibernate.BIG_DECIMAL.getName(), Hibernate.BIG_DECIMAL );
    basics.putHibernate.BIG_INTEGER.getName(), Hibernate.BIG_INTEGER );
    basics.putHibernate.SERIALIZABLE.getName(), Hibernate.SERIALIZABLE );
    basics.putHibernate.OBJECT.getName(), Hibernate.OBJECT );
    basics.putBoolean.class.getName(), Hibernate.BOOLEAN );
    basics.putLong.class.getName(), Hibernate.LONG );
    basics.putShort.class.getName(), Hibernate.SHORT );
    basics.putInteger.class.getName(), Hibernate.INTEGER );
    basics.putByte.class.getName(), Hibernate.BYTE );
    basics.putFloat.class.getName(), Hibernate.FLOAT );
    basics.putDouble.class.getName(), Hibernate.DOUBLE );
    basics.putCharacter.class.getName(), Hibernate.CHARACTER );
    basics.putString.class.getName(), Hibernate.STRING );
    basics.putjava.util.Date.class.getName(), Hibernate.TIMESTAMP );
    basics.putTime.class.getName(), Hibernate.TIME );
    basics.putTimestamp.class.getName(), Hibernate.TIMESTAMP );
    basics.putjava.sql.Date.class.getName(), Hibernate.DATE );
    basics.putBigDecimal.class.getName(), Hibernate.BIG_DECIMAL );
    basics.putBigInteger.class.getName(), Hibernate.BIG_INTEGER );
    basics.putLocale.class.getName(), Hibernate.LOCALE );
    basics.putCalendar.class.getName(), Hibernate.CALENDAR );
    basics.putGregorianCalendar.class.getName(), Hibernate.CALENDAR );
    if CurrencyType.CURRENCY_CLASS != null ) {
      basics.putCurrencyType.CURRENCY_CLASS.getName(), Hibernate.CURRENCY );
    }
    basics.putTimeZone.class.getName(), Hibernate.TIMEZONE );
    basics.putObject.class.getName(), Hibernate.OBJECT );
    basics.putClass.class.getName(), Hibernate.CLASS );
    basics.putbyte[].class.getName(), Hibernate.BINARY );
    basics.put"byte[]", Hibernate.BINARY );
    basics.putByte[].class.getName(), Hibernate.WRAPPER_BINARY );
    basics.put"Byte[]", Hibernate.WRAPPER_BINARY );
    basics.putchar[].class.getName(), Hibernate.CHAR_ARRAY );
    basics.put"char[]", Hibernate.CHAR_ARRAY );
    basics.putCharacter[].class.getName(), Hibernate.CHARACTER_ARRAY );
    basics.put"Character[]", Hibernate.CHARACTER_ARRAY );
    basics.putBlob.class.getName(), Hibernate.BLOB );
    basics.putClob.class.getName(), Hibernate.CLOB );
    basics.putSerializable.class.getName(), Hibernate.SERIALIZABLE );

    Type type = new AdaptedImmutableType(Hibernate.DATE);
    basics.puttype.getName(), type );
    type = new AdaptedImmutableType(Hibernate.TIME);
    basics.puttype.getName(), type );    
    type = new AdaptedImmutableType(Hibernate.TIMESTAMP);
    basics.puttype.getName(), type );
    type = new AdaptedImmutableTypenew DbTimestampType() );
    basics.puttype.getName(), type );
    type = new AdaptedImmutableType(Hibernate.CALENDAR);
    basics.puttype.getName(), type );
    type = new AdaptedImmutableType(Hibernate.CALENDAR_DATE);
    basics.puttype.getName(), type );
    type = new AdaptedImmutableType(Hibernate.SERIALIZABLE);
    basics.puttype.getName(), type );
    type = new AdaptedImmutableType(Hibernate.BINARY);
    basics.puttype.getName(), type );
    
    BASIC_TYPES = Collections.unmodifiableMapbasics );
  }

  private TypeFactory() {
    throw new UnsupportedOperationException();
  }

  /**
   * A one-to-one association type for the given class
   */
  public static EntityType oneToOne(
      String persistentClass,
      ForeignKeyDirection foreignKeyType,
      String uniqueKeyPropertyName, 
      boolean lazy, 
      boolean unwrapProxy,
      boolean isEmbeddedInXML, 
      String entityName, 
      String propertyName
  ) {
    return new OneToOneType(
        persistentClass,
        foreignKeyType,
        uniqueKeyPropertyName,
        lazy,
        unwrapProxy,
        isEmbeddedInXML,
        entityName,
        propertyName 
      );
  }

  /**
   * A many-to-one association type for the given class
   */
  public static EntityType manyToOne(String persistentClass) {
    return new ManyToOneTypepersistentClass );
  }

  /**
   * A many-to-one association type for the given class
   */
  public static EntityType manyToOne(String persistentClass, boolean lazy) {
    return new ManyToOneTypepersistentClass, lazy );
  }

  /**
   * A many-to-one association type for the given class
   */
  public static EntityType manyToOne(
      String persistentClass, 
      String uniqueKeyPropertyName,
      boolean lazy, 
      boolean unwrapProxy, 
      boolean isEmbeddedInXML,
      boolean ignoreNotFound
  ) {
    return new ManyToOneType
        persistentClass, 
        uniqueKeyPropertyName, 
        lazy,
        unwrapProxy, 
        isEmbeddedInXML,
        ignoreNotFound
      );
  }

  /**
   * Given the name of a Hibernate basic type, return an instance of
   <tt>org.hibernate.type.Type</tt>.
   */
  public static Type basic(String name) {
    return (TypeBASIC_TYPES.getname );
  }

  /**
   * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
   * Return an instance of <tt>org.hibernate.type.Type</tt>.
   */
  public static Type heuristicType(String typeNamethrows MappingException {
    return heuristicTypetypeName, null );
  }

  /**
   * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
   * Return an instance of <tt>org.hibernate.type.Type</tt>.
   */
  public static Type heuristicType(String typeName, Properties parameters)
      throws MappingException {
    Type type = TypeFactory.basictypeName );
    if type == null ) {
      Class typeClass;
      try {
        typeClass = ReflectHelper.classForNametypeName );
      }
      catch (ClassNotFoundException cnfe) {
        typeClass = null;
      }
      if typeClass != null ) {
        if Type.class.isAssignableFromtypeClass ) ) {
          try {
            type = (TypetypeClass.newInstance();
          }
          catch (Exception e) {
            throw new MappingException
                "Could not instantiate Type: " + typeClass.getName(),
                
              );
          }
          injectParameters(type, parameters);
        }
        else if CompositeUserType.class.isAssignableFromtypeClass ) ) {
          type = new CompositeCustomTypetypeClass, parameters );
        }
        else if UserType.class.isAssignableFromtypeClass ) ) {
          type = new CustomTypetypeClass, parameters );
        }
        else if Lifecycle.class.isAssignableFromtypeClass ) ) {
          type = Hibernate.entitytypeClass );
        }
        else if Serializable.class.isAssignableFromtypeClass ) ) {
          type = Hibernate.serializabletypeClass );
        }
      }
    }
    return type;

  }

  /**
   * The legacy contract.
   *
   @deprecated Use {@link #customCollection(String, java.util.Properties, String, String, boolean)} instead
   */
  public static CollectionType customCollection(
      String typeName,
      String role,
      String propertyRef,
      boolean embedded) {
    return customCollectiontypeName, null, role, propertyRef, embedded );
  }

  public static CollectionType customCollection(
      String typeName,
      Properties typeParameters,
      String role,
      String propertyRef,
      boolean embedded) {
    Class typeClass;
    try {
      typeClass = ReflectHelper.classForNametypeName );
    }
    catch ClassNotFoundException cnfe ) {
      throw new MappingException"user collection type class not found: " + typeName, cnfe );
    }
    CustomCollectionType result = new CustomCollectionTypetypeClass, role, propertyRef, embedded );
    if typeParameters != null ) {
      TypeFactory.injectParametersresult.getUserType(), typeParameters );
    }
    return result;
  }

  // Collection Types:

  public static CollectionType array(String role, String propertyRef, boolean embedded,
      Class elementClass) {
    return new ArrayTyperole, propertyRef, elementClass, embedded );
  }

  public static CollectionType list(String role, String propertyRef, boolean embedded) {
    return new ListTyperole, propertyRef, embedded );
  }

  public static CollectionType bag(String role, String propertyRef, boolean embedded) {
    return new BagTyperole, propertyRef, embedded );
  }

  public static CollectionType idbag(String role, String propertyRef, boolean embedded) {
    return new IdentifierBagTyperole, propertyRef, embedded );
  }

  public static CollectionType map(String role, String propertyRef, boolean embedded) {
    return new MapTyperole, propertyRef, embedded );
  }

  public static CollectionType orderedMap(String role, String propertyRef, boolean embedded) {
    return new OrderedMapTyperole, propertyRef, embedded );
  }

  public static CollectionType set(String role, String propertyRef, boolean embedded) {
    return new SetTyperole, propertyRef, embedded );
  }

  public static CollectionType orderedSet(String role, String propertyRef, boolean embedded) {
    return new OrderedSetTyperole, propertyRef, embedded );
  }

  public static CollectionType sortedMap(String role, String propertyRef, boolean embedded,
      Comparator comparator) {
    return new SortedMapTyperole, propertyRef, comparator, embedded );
  }

  public static CollectionType sortedSet(String role, String propertyRef, boolean embedded,
      Comparator comparator) {
    return new SortedSetTyperole, propertyRef, comparator, embedded );
  }

  public static void injectParameters(Object type, Properties parameters) {
    if (type instanceof ParameterizedType) {
      ( (ParameterizedTypetype ).setParameterValues(parameters);
    }
    else if parameters!=null && !parameters.isEmpty() ) {
      throw new MappingException(
          "type is not parameterized: " +
          type.getClass().getName()
        );
    }
  }


  // convenience methods relating to operations across arrays of types...

  /**
   * Deep copy a series of values from one array to another...
   *
   @param values The values to copy (the source)
   @param types The value types
   @param copy an array indicating which values to include in the copy
   @param target The array into which to copy the values
   @param session The orginating session
   */
  public static void deepCopy(
      final Object[] values,
      final Type[] types,
      final boolean[] copy,
      final Object[] target,
      final SessionImplementor session) {
    for int i = 0; i < types.length; i++ ) {
      if copy[i] ) {
        if values[i== LazyPropertyInitializer.UNFETCHED_PROPERTY
          || values[i== BackrefPropertyAccessor.UNKNOWN ) {
          target[i= values[i];
        }
        else {
          target[i= types[i].deepCopyvalues[i], session.getEntityMode(), session
            .getFactory() );
        }
      }
    }
  }

  /**
   * Apply the {@link Type#beforeAssemble} operation across a series of values.
   *
   @param row The values
   @param types The value types
   @param session The orginating session
   */
  public static void beforeAssemble(
      final Serializable[] row,
      final Type[] types,
      final SessionImplementor session) {
    for int i = 0; i < types.length; i++ ) {
      if row[i!= LazyPropertyInitializer.UNFETCHED_PROPERTY
        && row[i!= BackrefPropertyAccessor.UNKNOWN ) {
        types[i].beforeAssemblerow[i], session );
      }
    }
  }

  /**
   * Apply the {@link Type#assemble} operation across a series of values.
   *
   @param row The values
   @param types The value types
   @param session The orginating session
   @param owner The entity "owning" the values
   @return The assembled state
   */
  public static Object[] assemble(
      final Serializable[] row,
      final Type[] types,
      final SessionImplementor session,
      final Object owner) {
    Object[] assembled = new Object[row.length];
    for int i = 0; i < types.length; i++ ) {
      if row[i== LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i== BackrefPropertyAccessor.UNKNOWN ) {
        assembled[i= row[i];
      }
      else {
        assembled[i= types[i].assemblerow[i], session, owner );
      }
    }
    return assembled;
  }

  /**
   * Apply the {@link Type#disassemble} operation across a series of values.
   *
   @param row The values
   @param types The value types
   @param nonCacheable An array indicating which values to include in the disassemled state
   @param session The orginating session
   @param owner The entity "owning" the values
   @return The disassembled state
   */
  public static Serializable[] disassemble(
      final Object[] row,
      final Type[] types,
      final boolean[] nonCacheable,
      final SessionImplementor session,
      final Object owner) {
    Serializable[] disassembled = new Serializable[row.length];
    for int i = 0; i < row.length; i++ ) {
      if nonCacheable!=null && nonCacheable[i] ) {
        disassembled[i= LazyPropertyInitializer.UNFETCHED_PROPERTY;
      }
      else if row[i== LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i== BackrefPropertyAccessor.UNKNOWN ) {
        disassembled[i(Serializablerow[i];
      }
      else {
        disassembled[i= types[i].disassemblerow[i], session, owner );
      }
    }
    return disassembled;
  }

  /**
   * Apply the {@link Type#replace} operation across a series of values.
   *
   @param original The source of the state
   @param target The target into which to replace the source values.
   @param types The value types
   @param session The orginating session
   @param owner The entity "owning" the values
   @param copyCache A map representing a cache of already replaced state
   @return The replaced state
   */
  public static Object[] replace(
      final Object[] original,
      final Object[] target,
      final Type[] types,
      final SessionImplementor session,
      final Object owner,
      final Map copyCache) {
    Object[] copied = new Object[original.length];
    for int i = 0; i < types.length; i++ ) {
      if original[i== LazyPropertyInitializer.UNFETCHED_PROPERTY
        || original[i== BackrefPropertyAccessor.UNKNOWN ) {
        copied[i= target[i];
      }
      else {
        copied[i= types[i].replaceoriginal[i], target[i], session, owner, copyCache );
      }
    }
    return copied;
  }

  /**
   * Apply the {@link Type#replace} operation across a series of values.
   *
   @param original The source of the state
   @param target The target into which to replace the source values.
   @param types The value types
   @param session The orginating session
   @param owner The entity "owning" the values
   @param copyCache A map representing a cache of already replaced state
   @param foreignKeyDirection FK directionality to be applied to the replacement
   @return The replaced state
   */
  public static Object[] replace(
      final Object[] original,
      final Object[] target,
      final Type[] types,
      final SessionImplementor session,
      final Object owner,
      final Map copyCache,
      final ForeignKeyDirection foreignKeyDirection) {
    Object[] copied = new Object[original.length];
    for int i = 0; i < types.length; i++ ) {
      if original[i== LazyPropertyInitializer.UNFETCHED_PROPERTY
        || original[i== BackrefPropertyAccessor.UNKNOWN ) {
        copied[i= target[i];
      }
      else {
        copied[i= types[i].replaceoriginal[i], target[i], session, owner, copyCache, foreignKeyDirection );
      }
    }
    return copied;
  }

  /**
   * Apply the {@link Type#replace} operation across a series of values, as
   * long as the corresponding {@link Type} is an association.
   <p/>
   * If the corresponding type is a component type, then apply {@link #replaceAssociations}
   * accross the component subtypes but do not replace the component value itself.
   *
   @param original The source of the state
   @param target The target into which to replace the source values.
   @param types The value types
   @param session The orginating session
   @param owner The entity "owning" the values
   @param copyCache A map representing a cache of already replaced state
   @param foreignKeyDirection FK directionality to be applied to the replacement
   @return The replaced state
   */
  public static Object[] replaceAssociations(
      final Object[] original,
      final Object[] target,
      final Type[] types,
      final SessionImplementor session,
      final Object owner,
      final Map copyCache,
      final ForeignKeyDirection foreignKeyDirection) {
    Object[] copied = new Object[original.length];
    for int i = 0; i < types.length; i++ ) {
      if original[i== LazyPropertyInitializer.UNFETCHED_PROPERTY
          || original[i== BackrefPropertyAccessor.UNKNOWN ) {
        copied[i= target[i];
      }
      else if types[i].isComponentType() ) {
        // need to extract the component values and check for subtype replacements...
        AbstractComponentType componentType = AbstractComponentType types[i];
        Type[] subtypes = componentType.getSubtypes();
        Object[] origComponentValues = original[i== null new Object[subtypes.length: componentType.getPropertyValuesoriginal[i], session );
        Object[] targetComponentValues = target[i== null new Object[subtypes.length: componentType.getPropertyValuestarget[i], session );
        replaceAssociationsorigComponentValues, targetComponentValues, subtypes, session, null, copyCache, foreignKeyDirection );
        copied[i= target[i];
      }
      else if !types[i].isAssociationType() ) {
        copied[i= target[i];
      }
      else {
        copied[i= types[i].replaceoriginal[i], target[i], session, owner, copyCache, foreignKeyDirection );
      }
    }
    return copied;
  }


  /**
   * Determine if any of the given field values are dirty, returning an array containing 
   * indices of the dirty fields.
   <p/>
   * If it is determined that no fields are dirty, null is returned.
   *
   @param properties The property definitions 
   @param currentState The current state of the entity
   @param previousState The baseline state of the entity
   @param includeColumns Columns to be included in the dirty checking, per property
   @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
   @param session The session from which the dirty check request originated.
   @return Array containing indices of the dirty properties, or null if no properties considered dirty.
   */
  public static int[] findDirty(
      final StandardProperty[] properties, 
      final Object[] currentState,
      final Object[] previousState,
      final boolean[][] includeColumns,
      final boolean anyUninitializedProperties,
      final SessionImplementor session) {
    int[] results = null;
    int count = 0;
    int span = properties.length;

    for int i = 0; i < span; i++ ) {
      final boolean dirty = currentState[i!= LazyPropertyInitializer.UNFETCHED_PROPERTY
          && properties[i].isDirtyCheckableanyUninitializedProperties )
          && properties[i].getType().isDirtypreviousState[i], currentState[i], includeColumns[i], session );
      if dirty ) {
        if results == null ) {
          results = new int[span];
        }
        results[count++= i;
      }
    }

    if count == ) {
      return null;
    }
    else {
      int[] trimmed = new int[count];
      System.arraycopyresults, 0, trimmed, 0, count );
      return trimmed;
    }
  }

  /**
   * Determine if any of the given field values are modified, returning an array containing
   * indices of the modified fields.
   <p/>
   * If it is determined that no fields are dirty, null is returned.
   *
   @param properties The property definitions
   @param currentState The current state of the entity
   @param previousState The baseline state of the entity
   @param includeColumns Columns to be included in the mod checking, per property
   @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
   @param session The session from which the dirty check request originated.
   @return Array containing indices of the modified properties, or null if no properties considered modified.
   */
  public static int[] findModified(
      final StandardProperty[] properties, 
      final Object[] currentState,
      final Object[] previousState,
      final boolean[][] includeColumns,
      final boolean anyUninitializedProperties, 
      final SessionImplementor session) {
    int[] results = null;
    int count = 0;
    int span = properties.length;

    for int i = 0; i < span; i++ ) {
      final boolean modified = currentState[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY
          && properties[i].isDirtyCheckable(anyUninitializedProperties)
          && properties[i].getType().isModifiedpreviousState[i], currentState[i], includeColumns[i], session );

      if modified ) {
        if results == null ) {
          results = new int[span];
        }
        results[count++= i;
      }
    }

    if count == ) {
      return null;
    }
    else {
      int[] trimmed = new int[count];
      System.arraycopyresults, 0, trimmed, 0, count );
      return trimmed;
    }
  }

}