Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » type » [javadoc | source]
    1   //$Id: EnumType.java 14736 2008-06-04 14:23:42Z hardy.ferentschik $
    2   package org.hibernate.type;
    3   
    4   import java.io.IOException;
    5   import java.io.ObjectInputStream;
    6   import java.io.Serializable;
    7   import java.lang.reflect.Method;
    8   import java.sql.PreparedStatement;
    9   import java.sql.ResultSet;
   10   import java.sql.SQLException;
   11   import java.sql.Types;
   12   import java.util.HashMap;
   13   import java.util.Map;
   14   import java.util.Properties;
   15   
   16   import org.hibernate.AssertionFailure;
   17   import org.hibernate.HibernateException;
   18   import org.hibernate.annotations.common.util.StringHelper;
   19   import org.hibernate.usertype.EnhancedUserType;
   20   import org.hibernate.usertype.ParameterizedType;
   21   import org.hibernate.util.ReflectHelper;
   22   import org.slf4j.Logger;
   23   import org.slf4j.LoggerFactory;
   24   
   25   /**
   26    * Enum type mapper
   27    * Try and find the appropriate SQL type depending on column metadata
   28    *
   29    * @author Emmanuel Bernard
   30    */
   31   //TODO implements readobject/writeobject to recalculate the enumclasses
   32   public class EnumType implements EnhancedUserType, ParameterizedType, Serializable {
   33   	/**
   34   	 * This is the old scheme where logging of parameter bindings and value extractions
   35   	 * was controlled by the trace level enablement on the 'org.hibernate.type' package...
   36   	 * <p/>
   37   	 * Originally was cached such because of performance of looking up the logger each time
   38   	 * in order to check the trace-enablement.  Driving this via a central Log-specific class
   39   	 * would alleviate that performance hit, and yet still allow more "normal" logging usage/config.
   40   	 */
   41   	private static final boolean IS_VALUE_TRACING_ENABLED = LoggerFactory.getLogger( StringHelper.qualifier( Type.class.getName() ) ).isTraceEnabled();
   42   	private transient Logger log;
   43   
   44   	private Logger log() {
   45   		if ( log == null ) {
   46   			log = LoggerFactory.getLogger( getClass() );
   47   		}
   48   		return log;
   49   	}
   50   
   51   	public static final String ENUM = "enumClass";
   52   	public static final String SCHEMA = "schema";
   53   	public static final String CATALOG = "catalog";
   54   	public static final String TABLE = "table";
   55   	public static final String COLUMN = "column";
   56   	public static final String TYPE = "type";
   57   
   58   	private static Map<Class, Object[]> enumValues = new HashMap<Class, Object[]>();
   59   
   60   	private Class<? extends Enum> enumClass;
   61   	private String column;
   62   	private String table;
   63   	private String catalog;
   64   	private String schema;
   65   	private boolean guessed = false;
   66   	private int sqlType = Types.INTEGER; //before any guessing
   67   
   68   	public int[] sqlTypes() {
   69   		return new int[]{sqlType};
   70   	}
   71   
   72   	public Class returnedClass() {
   73   		return enumClass;
   74   	}
   75   
   76   	public boolean equals(Object x, Object y) throws HibernateException {
   77   		return x == y;
   78   	}
   79   
   80   	public int hashCode(Object x) throws HibernateException {
   81   		return x == null ? 0 : x.hashCode();
   82   	}
   83   
   84   	public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
   85   		Object object = rs.getObject( names[0] );
   86   		if ( rs.wasNull() ) {
   87   			if ( IS_VALUE_TRACING_ENABLED ) {
   88   				log().debug( "Returning null as column {}", names[0] );
   89   			}
   90   			return null;
   91   		}
   92   		if ( object instanceof Number ) {
   93   			Object[] values = enumValues.get( enumClass );
   94   			if ( values == null ) throw new AssertionFailure( "enumValues not preprocessed: " + enumClass );
   95   			int ordinal = ( (Number) object ).intValue();
   96   			if ( ordinal < 0 || ordinal >= values.length ) {
   97   				throw new IllegalArgumentException( "Unknown ordinal value for enum " + enumClass + ": " + ordinal );
   98   			}
   99   			if ( IS_VALUE_TRACING_ENABLED ) {
  100   				log().debug( "Returning '{}' as column {}", ordinal, names[0] );
  101   			}
  102   			return values[ordinal];
  103   		}
  104   		else {
  105   			String name = (String) object;
  106   			if ( IS_VALUE_TRACING_ENABLED ) {
  107   				log().debug( "Returning '{}' as column {}", name, names[0] );
  108   			}
  109   			try {
  110   				return Enum.valueOf( enumClass, name );
  111   			}
  112   			catch (IllegalArgumentException iae) {
  113   				throw new IllegalArgumentException( "Unknown name value for enum " + enumClass + ": " + name, iae );
  114   			}
  115   		}
  116   	}
  117   
  118   	public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
  119   		//if (!guessed) guessType( st, index );
  120   		if ( value == null ) {
  121   			if ( IS_VALUE_TRACING_ENABLED ) log().debug( "Binding null to parameter: {}", index );
  122   			st.setNull( index, sqlType );
  123   		}
  124   		else {
  125   			boolean isOrdinal = isOrdinal( sqlType );
  126   			if ( isOrdinal ) {
  127   				int ordinal = ( (Enum) value ).ordinal();
  128   				if ( IS_VALUE_TRACING_ENABLED ) {
  129   					log().debug( "Binding '{}' to parameter: {}", ordinal, index );
  130   				}
  131   				st.setObject( index, Integer.valueOf( ordinal ), sqlType );
  132   			}
  133   			else {
  134   				String enumString = ( (Enum) value ).name();
  135   				if ( IS_VALUE_TRACING_ENABLED ) {
  136   					log().debug( "Binding '{}' to parameter: {}", enumString, index );
  137   				}
  138   				st.setObject( index, enumString, sqlType );
  139   			}
  140   		}
  141   	}
  142   
  143   	private boolean isOrdinal(int paramType) {
  144   		switch ( paramType ) {
  145   			case Types.INTEGER:
  146   			case Types.NUMERIC:
  147   			case Types.SMALLINT:
  148   			case Types.TINYINT:
  149   			case Types.BIGINT:
  150   			case Types.DECIMAL: //for Oracle Driver
  151   			case Types.DOUBLE:  //for Oracle Driver
  152   			case Types.FLOAT:   //for Oracle Driver
  153   				return true;
  154   			case Types.CHAR:
  155   			case Types.LONGVARCHAR:
  156   			case Types.VARCHAR:
  157   				return false;
  158   			default:
  159   				throw new HibernateException( "Unable to persist an Enum in a column of SQL Type: " + paramType );
  160   		}
  161   	}
  162   
  163   	public Object deepCopy(Object value) throws HibernateException {
  164   		return value;
  165   	}
  166   
  167   	public boolean isMutable() {
  168   		return false;
  169   	}
  170   
  171   	public Serializable disassemble(Object value) throws HibernateException {
  172   		return (Serializable) value;
  173   	}
  174   
  175   	public Object assemble(Serializable cached, Object owner) throws HibernateException {
  176   		return cached;
  177   	}
  178   
  179   	public Object replace(Object original, Object target, Object owner) throws HibernateException {
  180   		return original;
  181   	}
  182   
  183   	public void setParameterValues(Properties parameters) {
  184   		String enumClassName = parameters.getProperty( ENUM );
  185   		try {
  186   			enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
  187   		}
  188   		catch (ClassNotFoundException exception) {
  189   			throw new HibernateException( "Enum class not found", exception );
  190   		}
  191   		//this is threadsafe to do it here, setParameterValues() is called sequencially
  192   		initEnumValue();
  193   		//nullify unnullified properties yuck!
  194   		schema = parameters.getProperty( SCHEMA );
  195   		if ( "".equals( schema ) ) schema = null;
  196   		catalog = parameters.getProperty( CATALOG );
  197   		if ( "".equals( catalog ) ) catalog = null;
  198   		table = parameters.getProperty( TABLE );
  199   		column = parameters.getProperty( COLUMN );
  200   		String type = parameters.getProperty( TYPE );
  201   		if ( type != null ) {
  202   			sqlType = Integer.decode( type ).intValue();
  203   			guessed = true;
  204   		}
  205   	}
  206   
  207   	private void initEnumValue() {
  208   		Object[] values = enumValues.get( enumClass );
  209   		if ( values == null ) {
  210   			try {
  211   				Method method = null;
  212   				method = enumClass.getDeclaredMethod( "values", new Class[0] );
  213   				values = (Object[]) method.invoke( null, new Object[0] );
  214   				enumValues.put( enumClass, values );
  215   			}
  216   			catch (Exception e) {
  217   				throw new HibernateException( "Error while accessing enum.values(): " + enumClass, e );
  218   			}
  219   		}
  220   	}
  221   
  222   	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  223   		//FIXME Hum, I think I break the thread safety here
  224   		ois.defaultReadObject();
  225   		initEnumValue();
  226   	}
  227   
  228   	public String objectToSQLString(Object value) {
  229   		boolean isOrdinal = isOrdinal( sqlType );
  230   		if ( isOrdinal ) {
  231   			int ordinal = ( (Enum) value ).ordinal();
  232   			return Integer.toString( ordinal );
  233   		}
  234   		else {
  235   			return '\'' + ( (Enum) value ).name() + '\'';
  236   		}
  237   	}
  238   
  239   	public String toXMLString(Object value) {
  240   		boolean isOrdinal = isOrdinal( sqlType );
  241   		if ( isOrdinal ) {
  242   			int ordinal = ( (Enum) value ).ordinal();
  243   			return Integer.toString( ordinal );
  244   		}
  245   		else {
  246   			return ( (Enum) value ).name();
  247   		}
  248   	}
  249   
  250   	public Object fromXMLString(String xmlValue) {
  251   		try {
  252   			int ordinal = Integer.parseInt( xmlValue );
  253   			Object[] values = enumValues.get( enumClass );
  254   			if ( values == null ) throw new AssertionFailure( "enumValues not preprocessed: " + enumClass );
  255   			if ( ordinal < 0 || ordinal >= values.length ) {
  256   				throw new IllegalArgumentException( "Unknown ordinal value for enum " + enumClass + ": " + ordinal );
  257   			}
  258   			return values[ordinal];
  259   		}
  260   		catch(NumberFormatException e) {
  261   			try {
  262   				return Enum.valueOf( enumClass, xmlValue );
  263   			}
  264   			catch (IllegalArgumentException iae) {
  265   				throw new IllegalArgumentException( "Unknown name value for enum " + enumClass + ": " + xmlValue, iae );
  266   			}
  267   		}
  268   	}
  269   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » type » [javadoc | source]