/* * Copyright 2010, 2011 Christopher Pheby * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jadira.usertype.spi.shared; import static org.jadira.usertype.spi.utils.reflection.ArrayUtils.copyOf; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.Type; import org.hibernate.usertype.CompositeUserType; import org.jadira.usertype.spi.utils.reflection.TypeHelper; public abstract class AbstractMultiColumnUserType<T> extends AbstractUserType implements CompositeUserType, Serializable { private static final long serialVersionUID = -8258683760413283329L; private int[] sqlTypes; private Type[] hibernateTypes; /* DefaultPropertyNames is currently not being used */ private String[] defaultPropertyNames; public AbstractMultiColumnUserType() { initialise(); } protected final void initialise() { initialiseSqlTypes(); initialiseHibernateTypes(); initialiseDefaultPropertyNames(); } private void initialiseDefaultPropertyNames() { Map<String, Integer> nameCount = new HashMap<String, Integer>(); defaultPropertyNames = new String[getColumnMappers().length]; for (int i = 0; i < defaultPropertyNames.length; i++) { String className = hibernateTypes[i].getClass().getSimpleName(); if (className.endsWith("Type")) { className = className.substring(0, className.length() - 4); } String name = className.toLowerCase(); final Integer count; if (nameCount.containsKey(name)) { Integer oldCount = nameCount.get(name); count = oldCount.intValue() + 1; defaultPropertyNames[i] = name + count; } else { count = 1; defaultPropertyNames[i] = name; } nameCount.put(name, count); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void initialiseHibernateTypes() { hibernateTypes = new Type[getColumnMappers().length]; for (int i = 0; i < hibernateTypes.length; i++) { hibernateTypes[i] = new ColumnMapperSingleColumnTypeAdapter(getColumnMappers()[i]); } } private void initialiseSqlTypes() { sqlTypes = new int[getColumnMappers().length]; for (int i = 0; i < sqlTypes.length; i++) { sqlTypes[i] = getColumnMappers()[i].getSqlType(); } } public int[] sqlTypes() { return copyOf(sqlTypes); } @SuppressWarnings("unchecked") @Override public Class<T> returnedClass() { return (Class<T>) TypeHelper.getTypeArguments(AbstractMultiColumnUserType.class, getClass()).get(0); } protected abstract ColumnMapper<?, ?>[] getColumnMappers(); @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public T nullSafeGet(ResultSet resultSet, String[] strings, SharedSessionContractImplementor session, Object object) throws SQLException { beforeNullSafeOperation(session); try { Object[] convertedColumns = new Object[getColumnMappers().length]; for (int getIndex = 0; getIndex < getColumnMappers().length; getIndex++) { ColumnMapper nextMapper = getColumnMappers()[getIndex]; final Object converted = nextMapper.getHibernateType().nullSafeGet(resultSet, strings[getIndex], session, object); if (converted != null) { convertedColumns[getIndex] = nextMapper.fromNonNullValue(converted); } } for (int i = 0; i < convertedColumns.length; i++) { if (convertedColumns[i] != null) { return fromConvertedColumns(convertedColumns); } } return null; } finally { afterNullSafeOperation(session); } } protected abstract T fromConvertedColumns(Object[] convertedColumns); protected abstract Object[] toConvertedColumns(T value); @SuppressWarnings("unchecked") @Override public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SharedSessionContractImplementor session) throws SQLException { beforeNullSafeOperation(session); try { final Object[] valuesToSet = new Object[getColumnMappers().length]; if (value != null) { final T myValue = (T) value; Object[] convertedColumns = toConvertedColumns(myValue); for (int cIdx = 0; cIdx < valuesToSet.length; cIdx++) { @SuppressWarnings("rawtypes") ColumnMapper nextMapper = getColumnMappers()[cIdx]; valuesToSet[cIdx] = nextMapper.toNonNullValue(convertedColumns[cIdx]); } } for (int setIndex = 0; setIndex < valuesToSet.length; setIndex++) { @SuppressWarnings("rawtypes") ColumnMapper nextMapper = getColumnMappers()[setIndex]; nextMapper.getHibernateType().nullSafeSet(preparedStatement, valuesToSet[setIndex], index + setIndex, session); } } finally { afterNullSafeOperation(session); } } @Override public String[] getPropertyNames() { return defaultPropertyNames; } @Override public Type[] getPropertyTypes() { return copyOf(hibernateTypes); } @Override public Object getPropertyValue(Object component, int property) throws HibernateException { if (!returnedClass().isAssignableFrom(component.getClass())) { throw new HibernateException("getPropertyValue called with incorrect class: {" + component.getClass() + "}"); } @SuppressWarnings("unchecked") Object[] cols = toConvertedColumns((T) component); return cols[property]; } @Override public void setPropertyValue(Object component, int property, Object value) throws HibernateException { throw new HibernateException("Called setPropertyValue on an immutable type {" + component.getClass() + "}"); } @Override public Serializable disassemble(Object value, SharedSessionContractImplementor session) throws HibernateException { return super.disassemble(value); } @Override public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException { return super.assemble(cached, owner); } @Override public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner) throws HibernateException { return super.replace(original, target, owner); } }