Java tutorial
/* Pivotal 5 Solutions Inc. - Core Java library for all other Pivotal Java Modules. * * Copyright (C) 2011 KASRA RASAEE * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.p5solutions.core.jpa.orm; import java.io.IOException; import java.io.Reader; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import javax.annotation.Resource; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.transaction.InvalidTransactionException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import com.p5solutions.core.aop.Targetable; import com.p5solutions.core.jpa.orm.exceptions.TypeConversionException; import com.p5solutions.core.utils.NumberUtils; import com.p5solutions.core.utils.ReflectionUtility; /** * The Class ConversionUtilityImpl. */ public class ConversionUtilityImpl implements ConversionUtility { public static final String DATE_FORMAT_STRING = "yyyy-MM-dd"; private SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT_STRING); /** The entity utility. */ private EntityUtility entityUtility; /** The conversion service. */ private ConversionService conversionService; /** The LOGGER. */ protected static Log logger = LogFactory.getLog(ConversionUtility.class); /** * Convert string. * * @param value * the value * @param clazz * the clazz * @return the object */ protected Object convertString(String value, Class<?> clazz) { if (ReflectionUtility.isBooleanClass(clazz)) { return Boolean.valueOf(value); } else if (ReflectionUtility.isShortClass(clazz)) { return Short.valueOf(value); } else if (ReflectionUtility.isIntegerClass(clazz)) { return Integer.valueOf(value); } else if (ReflectionUtility.isLongClass(clazz)) { return Long.valueOf(value); } else if (ReflectionUtility.isFloatClass(clazz)) { return Float.valueOf(value); } else if (ReflectionUtility.isDoubleClass(clazz)) { return Double.valueOf(value); } else if (ReflectionUtility.isBigDecimalClass(clazz)) { return new BigDecimal(value); } else if (ReflectionUtility.isBigIntegerClass(clazz)) { return new BigInteger(value); } else if (ReflectionUtility.isByteClass(clazz)) { return Byte.valueOf(value); } else if (ReflectionUtility.isDateOrTimestamp(clazz)) { try { return DATE_FORMAT.parse(value); } catch (ParseException e) { logger.error("Unable to parse " + value + " using date formatter " + DATE_FORMAT_STRING); return value; } } else { logger.warn("Unable to identify appropriate conversion strategy for value " + value + " using class type " + clazz); } return value; } /** * @see com.p5solutions.core.jpa.orm.ConversionUtility#convert(java.lang.Object, * java.lang.Class) */ @Override public Object convert(Object value, Class<?> clazz) { if (value instanceof String && conversionService == null) { return convertString((String) value, clazz); } // if (value instanceof String && ReflectionUtility.isBasicClass(clazz)) { // return convertString((String) value, clazz); // } // Use the Spring Conversion service and try to map the // values TypeDescriptor sourceType = TypeDescriptor.forObject(value); // setup the type descriptor and pass it to the converter TypeDescriptor targetType = TypeDescriptor.valueOf(clazz); Class<?> fromClass = value.getClass(); //Boolean convertible = conversionService.canConvert(fromClass, clazz); Boolean convertible = conversionService.canConvert(sourceType, targetType); if (convertible) { return conversionService.convert(value, sourceType, targetType); } return value; } /** * @see com.p5solutions.core.jpa.orm.ConversionUtility#convert(java.lang.Object, * java.lang.String) */ @Override public Object convert(Object value, String className) { try { Class<?> toClass = Class.forName(className); return convert(value, toClass); } catch (ClassNotFoundException e) { logger.error("Unable to convert value " + value + " to class name " + className); } return value; } /** * Convert number. * * @param pb * the pb * @param value * the value * @param targetType * the target type * @return the object * @throws TypeConversionException * the type conversion exception * @see com.p5solutions.core.jpa.orm.ConversionUtility#convertNumber(java.lang.Number, * java.lang.Class) */ @Override public Object convertNumber(ParameterBinder pb, Number value, Class<?> targetType) throws TypeConversionException { // any ParameterBinder related conversions can go here. // for example, the OID value in postgresql = a byte stream. return convertNumber(value, targetType); } /** * Convert number. * * @param value * the value * @param targetType * the target type * @return the object * @throws TypeConversionException * the type conversion exception */ private Object convertNumber(Number value, Class<?> targetType) throws TypeConversionException { if (ReflectionUtility.isBooleanClass(targetType)) { if (value.intValue() == 1) { return Boolean.TRUE; } else if (value.intValue() == 0) { return Boolean.FALSE; } throw new TypeConversionException("Unable to convert value of " + value + " to Boolean type!"); } else if (ReflectionUtility.isShortClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Short.class); } else if (ReflectionUtility.isIntegerClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Integer.class); } else if (ReflectionUtility.isLongClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Long.class); } else if (ReflectionUtility.isFloatClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Float.class); } else if (ReflectionUtility.isDoubleClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Double.class); } else if (ReflectionUtility.isBigDecimalClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, BigDecimal.class); } else if (ReflectionUtility.isBigIntegerClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, BigInteger.class); } else if (ReflectionUtility.isByteClass(targetType)) { return NumberUtils.convertNumberToTargetClass(value, Byte.class); } return value.intValue(); } /** * Convert timestamp. * * @param pb * the pb * @param timestamp * the timestamp * @param targetType * the target type * @return the object */ public Object convertTimestamp(ParameterBinder pb, Timestamp timestamp, Class<?> targetType) { // TODO probably needs further checking based on JPA annotations, some // timezone issues??? if (ReflectionUtility.isDate(targetType)) { // Check for Temporal if (pb != null) { Temporal temporal = pb.getTemporal(); if (temporal != null) { Date converted = timestamp; if (TemporalType.DATE.equals(temporal.value())) { java.sql.Date dt = new java.sql.Date(timestamp.getTime()); return dt; } else if (TemporalType.TIME.equals(temporal.value())) { java.sql.Time tm = new java.sql.Time(timestamp.getTime()); return tm; } else if (TemporalType.TIMESTAMP.equals(temporal.value())) { } return converted; } } Date test = new Date(timestamp.getTime()); return test; // return (Date) timestamp; } else if (ReflectionUtility.isStringClass(targetType)) { // TODO needs to be formatted based on the Format defined by the // 'custom?' // Format annotation ???? return timestamp.toLocaleString(); } else if (ReflectionUtility.isLongClass(targetType)) { } return timestamp; } /** * Convert blob. * * @param blob * the blob * @param targetType * the target type * @return the object */ public Object convertBLOB(Blob blob, Class<?> targetType) { if (ReflectionUtility.isStringClass(targetType)) { // Reader reader = clob.getCharacterStream(); // TODO charset needs to be passed in from the resultset ?? // CharBuffer cb = new CharBuffer(CharacterSet.AL32UTF8_CHARSET); // java.nio.CharBuffer cb = new java.nio.CharBuffer(); // reader.read(target); } else if (ReflectionUtility.isByteArray(targetType)) { } return blob; } /** * Convert clob. * * @param clob * the clob * @param targetType * the target type * @return the object */ public Object convertCLOB(Clob clob, Class<?> targetType) { if (ReflectionUtility.isStringClass(targetType)) { // TODO charset needs to be passed in from the resultset, or defined // as // part of the transaction template??? try { // TODO THIS NEEDS TO BE APPENDED IN UTF-8 ??? based on the // character // setting of the database??? Reader reader = clob.getCharacterStream(); StringBuilder output = new StringBuilder(); int r = -1; while ((r = reader.read()) >= 0) { output.append((char) r); } return output.toString(); } catch (SQLException e) { // TODO log it throw new RuntimeException(e); } catch (IOException e) { // TODO log it throw new RuntimeException(e); } } else if (ReflectionUtility.isByteArray(targetType)) { } return clob; } /* * (non-Javadoc) * * @see * com.p5solutions.core.jpa.orm.ConversionUtility#convert(com.p5solutions. * core.jpa.orm.ParameterBinder , java.lang.Object, java.lang.Class) */ @Override public Object convert(ParameterBinder pb, Object value, Class<?> targetType) throws TypeConversionException { return convert(pb, value, null, targetType); } /** * Convert simple value. * * @param pb * the pb * @param value * the value * @param bindingPath * the binding path * @param sourceType * the source type * @param targetType * the target type * @return the object * @throws TypeConversionException * the type conversion exception */ protected Object convertSimpleValue(ParameterBinder pb, Object value, String bindingPath, Class<?> sourceType, Class<?> targetType) throws TypeConversionException { if (ReflectionUtility.isClob(sourceType)) { value = convertCLOB((Clob) value, targetType); } else if (ReflectionUtility.isBlob(sourceType)) { value = convertBLOB((Blob) value, targetType); } else if (ReflectionUtility.isTimestamp(sourceType)) { value = convertTimestamp(pb, (Timestamp) value, targetType); } else if (ReflectionUtility.isNumberClass(sourceType)) { return convertNumber(pb, (Number) value, targetType); } else if (ReflectionUtility.isSerializableClass(sourceType) && ReflectionUtility.isSerializableClass(targetType)) { return value; } else { // TODO needs to have special exception thrown?? String msg = "Cannot convert from class type " + sourceType + " to target type " + targetType; if (bindingPath != null) { msg += " when using binding path " + bindingPath; } throw new TypeConversionException(msg); } return value; } /** * Convert complex value. * * @param pb * the pb * @param value * the value * @param sourceType * the source type * @param targetType * the target type * @return the object * @throws TypeConversionException * the type conversion exception */ protected Object convertComplexValue(ParameterBinder pb, Object value, Class<?> sourceType, Class<?> targetType) throws TypeConversionException { EntityDetail<?> entityDetail = getEntityUtility().getEntityDetail(sourceType); List<ParameterBinder> pkpbs = entityDetail.getPrimaryKeyParameterBinders(); if (pkpbs == null) { throw new NullPointerException("No primary key columns defined for table-entity of type " + sourceType); } if (pkpbs.size() > 1) { throw new RuntimeException(new InvalidTransactionException("Only surogate keys " + "supported when arguments by reference, and not value. " + "Entity type " + sourceType)); } ParameterBinder pkpb = pkpbs.get(0); // IDEALLY WE WANT TO DO THIS BY SOME SORT OF BINDING PATH. // ParameterBinder pb = // entityDetail.getParameterBinderByBindingPath(bindingPath); // if (pkpb != null) { Object newValue = ReflectionUtility.getValue(pkpb.getGetterMethod(), value); value = newValue; } return value; } /* * (non-Javadoc) * * @see * com.p5solutions.core.jpa.orm.ConversionUtility#convert(com.p5solutions. * core.jpa.orm.ParameterBinder , java.lang.Object, java.lang.String, * java.lang.Class) */ @Override public Object convert(ParameterBinder pb, Object value, String bindingPath, Class<?> targetType) throws TypeConversionException { // null values are null values if (value == null) { return value; } // in case proxied object was passed in get the target class (e.g. proxied // eagerly loaded type code object) if (value instanceof Targetable) { // the persistence layer is the simple-jpa implementation value = ((Targetable) value).getTarget(); } // get the source type and do the conversion Class<?> sourceType = value.getClass(); if (!isSameClass(value, targetType)) { value = convertSimpleValue(pb, value, bindingPath, sourceType, targetType); } else if (!ReflectionUtility.isBasicClass(sourceType)) { value = convertComplexValue(pb, value, sourceType, targetType); } // return converted value return value; } /** * Get the sql-type for this column, usually generated by the * {@link EntityUtility#buildColumnMetaDataAll()} * * @param pb * the {@link ParameterBinder} used to extract the sql-type * @return sql-type integer usually defined by {@link java.sql.Types} */ public int getSqlType(ParameterBinder pb) { return pb.getColumnMetaData().getColumnType(); } @Override public Object convertToSqlType(ParameterBinder pb, Object value) { // TODO basic conversion, for example, to_char string to timestamp, number // to string, or string to date, or whatever. return value; } /** * Checks if is same class. * * @param value * the value * @param targetType * the target type * @return true, if is same class * @see com.p5solutions.core.jpa.orm.ConversionUtility#isSameClass(java.lang.Object, * java.lang.Class) */ @Override public boolean isSameClass(Object value, Class<?> targetType) { if (value != null) { return value.getClass().equals(targetType); } return false; } /** * Gets the entity utility. * * @return the entity utility */ public EntityUtility getEntityUtility() { return entityUtility; } /** * Sets the entity utility. * * @param entityUtility * the new entity utility */ public void setEntityUtility(EntityUtility entityUtility) { this.entityUtility = entityUtility; } /** * @see com.p5solutions.core.jpa.orm.ConversionUtility#setConversionService(org.springframework.core.convert.ConversionService) */ public void setConversionService(ConversionService conversionService) { this.conversionService = conversionService; } }