PageRenderTime 28ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-hibernate3.6-extras/src/main/java/com/atlassian/hibernate/extras/type/GenericEnumUserType.java

https://bitbucket.org/atlassian/atlassian-hibernate-extras
Java | 294 lines | 185 code | 32 blank | 77 comment | 8 complexity | 7ad67774c048862a5ffcd185bce32418 MD5 | raw file
Possible License(s): LGPL-2.1
  1. package com.atlassian.hibernate.extras.type;
  2. import org.hibernate.HibernateException;
  3. import org.hibernate.type.BasicTypeRegistry;
  4. import org.hibernate.type.SingleColumnType;
  5. import org.hibernate.usertype.EnhancedUserType;
  6. import org.hibernate.usertype.ParameterizedType;
  7. import java.io.ObjectInputStream;
  8. import java.io.Serializable;
  9. import java.lang.reflect.Method;
  10. import java.sql.PreparedStatement;
  11. import java.sql.ResultSet;
  12. import java.sql.SQLException;
  13. import java.util.Properties;
  14. /**
  15. * Implements a generic enum user type identified/represented by a single identifier/column.
  16. * <p>
  17. * <ul>
  18. * <li>The enum type being represented by the certain user type must be set by using the 'enumClass' property.</li>
  19. * <li>The identifier representing a enum value is retrieved by the identifierMethod. The name of the identifier method
  20. * can be specified by the 'identifierMethod' property and by default the getId() method is used.</li>
  21. * <li>The identifier type is automatically determined by the return-type of the identifierMethod.</li>
  22. * <li>The valueOfMethod is the name of the static factory method returning the enumeration object being represented by
  23. * the given identifier. The valueOfMethod's name can be specified by setting the 'valueOfMethod' property. The default
  24. * valueOfMethod's name is 'fromId'.</li>
  25. * </ul>
  26. * <p>
  27. * Example of an enum type represented by an int value:
  28. * <pre><code>
  29. * package com.atlassian.demo;
  30. *
  31. * public enum SimpleNumber {
  32. * UNKNOWN(-1),
  33. * ZERO(0),
  34. * ONE(1),
  35. * TWO(2),
  36. * THREE(3);
  37. *
  38. * public int getId() {
  39. * return value;
  40. * }
  41. *
  42. * public SimpleNumber fromId(int value) {
  43. * switch(value) {
  44. * case 0: return ZERO;
  45. * case 1: return ONE;
  46. * case 2: return TWO;
  47. * case 3: return THREE;
  48. * default: return UNKNOWN;
  49. * }
  50. * }
  51. * }
  52. * </code></pre>
  53. * <p>
  54. * Using JPA, the mapping would look like this:
  55. * <pre><code>
  56. * &#064;Type(type = "com.atlassian.stash.internal.hibernate.GenericEnumUserType", parameters = {
  57. * &#064;Parameter(name = "enumClass", value = "com.atlassian.demo.SimpleNumber"),
  58. * &#064;Parameter(name = "identifierMethod", value = "getId"),
  59. * &#064;Parameter(name = "valueOfMethod", value = "fromId")})
  60. * private SimpleNumber randomNumber;
  61. * </code></pre>
  62. * <p>
  63. * In this example, the properties for the {@code GenericEnumUserType} are fully specified to make the example more
  64. * clear on the type's usage. However, because the identifier and valueOf methods follow standard naming, properties
  65. * for them may be omitted from the mapping to reduce configuration.
  66. *
  67. * @since 1.0
  68. */
  69. //NOTE: Deprecation warnings are from 3.6 referring to changes in Hibernate 4. Hibernate pre-deprecates things to
  70. // "announce" to their developers that their code is about to get broken. There is no mechanism available in
  71. // Hibernate 3.6 to avoid these warnings, so they must simply be suppressed until we upgrade to Hibernate 4.
  72. //SEE: https://hibernate.onjira.com/browse/HHH-5968
  73. @SuppressWarnings({"deprecation", "unused"})
  74. public class GenericEnumUserType implements EnhancedUserType, ParameterizedType, Serializable
  75. {
  76. private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "getId";
  77. private static final String DEFAULT_VALUE_OF_METHOD_NAME = "fromId";
  78. private static final Class[] NULL_CLASS_VARARG = null;
  79. private static final Object[] NULL_OBJECT_VARARG = null;
  80. private static final char SINGLE_QUOTE = '\'';
  81. private Class<? extends Enum> enumClass;
  82. private Method identifierMethod;
  83. private int[] sqlTypes;
  84. private SingleColumnType<Object> type;
  85. private Method valueOfMethod;
  86. public Object assemble(Serializable cached, Object owner) throws HibernateException
  87. {
  88. return cached;
  89. }
  90. public Object deepCopy(Object value) throws HibernateException
  91. {
  92. return value;
  93. }
  94. public Serializable disassemble(Object value) throws HibernateException
  95. {
  96. return (Serializable) value;
  97. }
  98. public boolean equals(Object x, Object y) throws HibernateException
  99. {
  100. return x == y;
  101. }
  102. public Object fromXMLString(String xmlValue)
  103. {
  104. return Enum.valueOf(enumClass, xmlValue);
  105. }
  106. public int hashCode(Object x) throws HibernateException
  107. {
  108. return x.hashCode();
  109. }
  110. public boolean isMutable()
  111. {
  112. return false;
  113. }
  114. public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
  115. {
  116. Object identifier = type.nullSafeGet(rs, names[0]);
  117. if (identifier == null || rs.wasNull())
  118. {
  119. return null;
  120. }
  121. try
  122. {
  123. return valueOfMethod.invoke(enumClass, identifier);
  124. }
  125. catch (Exception exception)
  126. {
  127. String msg = "Exception while invoking valueOfMethod [" + valueOfMethod.getName() +
  128. "] of Enum class [" + enumClass.getName() + "] with argument of type [" +
  129. identifier.getClass().getName() + "], value=[" + identifier + "]";
  130. throw new HibernateException(msg, exception);
  131. }
  132. }
  133. public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
  134. {
  135. if (value == null)
  136. {
  137. st.setNull(index, sqlTypes[0]);
  138. }
  139. else
  140. {
  141. try
  142. {
  143. Object identifier = identifierMethod.invoke(value, NULL_OBJECT_VARARG);
  144. type.set(st, identifier, index);
  145. }
  146. catch (Exception exception)
  147. {
  148. String msg = "Exception while invoking identifierMethod [" + identifierMethod.getName() +
  149. "] of Enum class [" + enumClass.getName() +
  150. "] with argument of type [" + value.getClass().getName() + "], value=[" + value + "]";
  151. throw new HibernateException(msg, exception);
  152. }
  153. }
  154. }
  155. public String objectToSQLString(Object value)
  156. {
  157. return SINGLE_QUOTE + ((Enum) value).name() + SINGLE_QUOTE;
  158. }
  159. public Object replace(Object original, Object target, Object owner) throws HibernateException
  160. {
  161. return original;
  162. }
  163. public Class returnedClass()
  164. {
  165. return enumClass;
  166. }
  167. public void setParameterValues(Properties parameters)
  168. {
  169. String enumClassName = parameters.getProperty("enumClass");
  170. String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
  171. String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
  172. initialize(enumClassName, identifierMethodName, valueOfMethodName);
  173. }
  174. public int[] sqlTypes()
  175. {
  176. return sqlTypes;
  177. }
  178. public String toXMLString(Object value)
  179. {
  180. return ((Enum<?>) value).name();
  181. }
  182. @SuppressWarnings("unchecked")
  183. private void initialize(String enumClassName, String identifierMethodName, String valueOfMethodName)
  184. {
  185. try
  186. {
  187. enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
  188. }
  189. catch (ClassNotFoundException exception)
  190. {
  191. throw new HibernateException("Enum class not found", exception);
  192. }
  193. try
  194. {
  195. identifierMethod = enumClass.getMethod(identifierMethodName, NULL_CLASS_VARARG);
  196. }
  197. catch (Exception exception)
  198. {
  199. throw new HibernateException("Failed to obtain identifier method", exception);
  200. }
  201. Class<?> identifierType = identifierMethod.getReturnType();
  202. try
  203. {
  204. valueOfMethod = enumClass.getMethod(valueOfMethodName, identifierType);
  205. }
  206. catch (Exception exception)
  207. {
  208. throw new HibernateException("Failed to obtain valueOf method", exception);
  209. }
  210. //TODO: We really shouldn't be instantiating this, but I don't know how to get the SessionImplementor here
  211. BasicTypeRegistry registry = new BasicTypeRegistry();
  212. type = (SingleColumnType<Object>) registry.getRegisteredType(identifierType.getName());
  213. if (type == null)
  214. {
  215. throw new HibernateException("Unsupported identifier type " + identifierType.getName());
  216. }
  217. sqlTypes = new int[]{type.sqlType()};
  218. }
  219. /**
  220. * Prevents attempts to deserialize the {@code GenericEnumUserType} directly, as {@link SerializationProxy}
  221. * should have been written in its place.
  222. *
  223. * @param stream ignored
  224. */
  225. private void readObject(ObjectInputStream stream)
  226. {
  227. throw new UnsupportedOperationException(getClass().getName() + " cannot be deserialized directly");
  228. }
  229. /**
  230. * Replaces this {@code GenericEnumUserType} (which is not really serializable due to the {@code Method} fields)
  231. * with a simple {@link SerializationProxy} containing the names for the enum class, the identifier method and
  232. * the valueOf method.
  233. *
  234. * @return a new {@link SerializationProxy} to be serialized in this type's place
  235. */
  236. private Object writeReplace()
  237. {
  238. return new SerializationProxy(enumClass.getName(), identifierMethod.getName(), valueOfMethod.getName());
  239. }
  240. /**
  241. * Simple proxy to serialize in place of the more complicated {@link GenericEnumUserType}. This proxy contains
  242. * all the data necessary to recreate/reinitialize the full {@link GenericEnumUserType}.
  243. */
  244. private static class SerializationProxy implements Serializable
  245. {
  246. private final String enumClassName;
  247. private final String identifierMethodName;
  248. private final String valueOfMethodName;
  249. private SerializationProxy(String enumClassName, String identifierMethodName, String valueOfMethodName)
  250. {
  251. this.enumClassName = enumClassName;
  252. this.identifierMethodName = identifierMethodName;
  253. this.valueOfMethodName = valueOfMethodName;
  254. }
  255. private Object readResolve()
  256. {
  257. GenericEnumUserType type = new GenericEnumUserType();
  258. type.initialize(enumClassName, identifierMethodName, valueOfMethodName);
  259. return type;
  260. }
  261. }
  262. }