// $HeadURL$ // $Id$ // // Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College. // // Screensaver is an open-source project developed by the ICCB-L and NSRB labs // at Harvard Medical School. This software is distributed under the terms of // the GNU General Public License. // TODO: this is not a model class, but rather a persistence-related class, as it is only concerned with how to convert // model types into values that can be persisted; it should be moved to e.h.m.s.db.usertypes package edu.harvard.med.screensaver.model; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.HashMap; import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.usertype.UserType; /** * A Hibernate <code>UserType</code> to map the {@link VocabularyTerm * VocabularyTerms}. This abstract superclass provides all the functionality * to make the <code>VocabularyTerms</code> work as Hibernate types. All it * needs to know are the <code>Enum.values()</code> array of vocabulary terms. * * <p> * * Let's say the name of the <code>VocabularyTerm</code> subclass is * <code>term.VTS</code>. In order to make <code>term.VTS</code> function * properly, two things need to be done. First, declare a static nested class * like so: * * <pre> * public static class UserType extends VocabularyUserType<VTS> { * public UserType() { * super(VTS.values()); * } * } * </pre> * * Second, every entity property that has type <code>term.VTS</code> must be * Hibernate-annotated with the <code>UserType</code>. For example: * * <pre> * @org.hibernate.annotations.Type( * type="term.VTS$UserType" * ) * </pre> * * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a> * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> */ abstract public class VocabularyUserType<VT extends VocabularyTerm> implements UserType { // private static fields private static final int[] SQL_TYPES = {Types.CLOB}; // private instance fields private Map<String,VT> _valueToTerm = new HashMap<String,VT>(); private Map<String,VT> _valueToTermCaseInsensitive = new HashMap<String,VT>(); // protected constructor protected VocabularyUserType(VT [] vocabularyTerms) { for (VT vocabularyTerm : vocabularyTerms) { _valueToTerm.put(vocabularyTerm.getValue(), vocabularyTerm); _valueToTermCaseInsensitive.put(vocabularyTerm.getValue().toLowerCase(), vocabularyTerm); } } // public method for general use public VT getTermForValue(String value) { return _valueToTerm.get(value); } public VT getTermForValueCaseInsensitive(String value) { if (value == null) { return null; } VT vt = _valueToTermCaseInsensitive.get(value.toLowerCase()); if (vt == null) { throw new IllegalArgumentException("'" + value + "' must be one of: " + _valueToTerm.keySet()); } return vt; } // public instance methods that implement UserType /* (non-Javadoc) * @see org.hibernate.usertype.UserType#sqlTypes() */ public int[] sqlTypes() { return SQL_TYPES; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#returnedClass() */ public Class returnedClass() { return VocabularyTerm.class; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#equals(java.lang.Object, java.lang.Object) */ public boolean equals(Object x, Object y) { if (x == y) { return true; } if (null == x || null == y) { return false; } return x.equals(y); } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#hashCode(java.lang.Object) */ public int hashCode(Object object) throws HibernateException { return object.hashCode(); } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#nullSafeGet(java.sql.ResultSet, java.lang.String[], java.lang.Object) */ public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException { String termName = resultSet.getString(names[0]); return resultSet.wasNull() ? null : _valueToTerm.get(termName); } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#nullSafeSet(java.sql.PreparedStatement, java.lang.Object, int) */ public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException, SQLException { if (value == null) { statement.setNull(index, Types.CLOB); } else { // TODO: value.toString() should be value.getValue(), otherwise concrete class must implement toString() as delegate to getValue(), which is not a documented requirement of this class statement.setString(index, value.toString()); } } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#deepCopy(java.lang.Object) */ public Object deepCopy(Object value) { return value; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#isMutable() */ public boolean isMutable() { return false; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#disassemble(java.lang.Object) */ public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#assemble(java.io.Serializable, java.lang.Object) */ public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } /* (non-Javadoc) * @see org.hibernate.usertype.UserType#replace(java.lang.Object, java.lang.Object, java.lang.Object) */ public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } }