/* * Copyright 2008 Pavel Syrtsov * * 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 com.sf.ddao.orm; import com.sf.ddao.DaoException; import com.sf.ddao.orm.rsmapper.ArrayRSMapper; import com.sf.ddao.orm.rsmapper.CollectionRSMapper; import com.sf.ddao.orm.rsmapper.MapRSMapper; import com.sf.ddao.orm.rsmapper.SingleRowRSMapper; import com.sf.ddao.orm.rsmapper.rowmapper.*; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; import java.sql.Blob; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Map; /** * Created by: Pavel Syrtsov * Date: Apr 14, 2007 * Time: 5:01:14 PM */ public class RSMapperFactoryRegistry { @SuppressWarnings({"UnusedDeclaration"}) public static RSMapperFactory create(Method method) { int paramIdx = findParameter(method); if (paramIdx >= 0) { return new ParameterBasedRSMapperFactory(paramIdx); } final RSMapper single = createReusable(method); return new ReusableRSMapperFactory(single); } public static RSMapper createReusable(Method method) { final UseRSMapper useRSMapper = method.getAnnotation(UseRSMapper.class); if (useRSMapper != null) { try { return useRSMapper.value().newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } Class<?> returnClass = method.getReturnType(); if (returnClass.isArray()) { Class itemType = returnClass.getComponentType(); RowMapperFactory rowMapperFactory = getRowMapperFactory(itemType); return new ArrayRSMapper(rowMapperFactory, itemType); } Type returnType = method.getGenericReturnType(); if (Collection.class.isAssignableFrom(returnClass)) { Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments(); Type itemType = actualTypeArguments[0]; return getCollectionORMapper(itemType); } if (Map.class.isAssignableFrom(returnClass)) { Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments(); Type keyType = actualTypeArguments[0]; Type valueType = actualTypeArguments[1]; RowMapperFactory keyMapperFactory = getScalarMapper(keyType, 1, true); RowMapperFactory valueMapperFactory = getRowMapperFactory(valueType, 2); return new MapRSMapper(keyMapperFactory, valueMapperFactory); } return new SingleRowRSMapper(getRowMapperFactory(returnType)); } public static int findParameter(Method method) { final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0, parameterAnnotationsLength = parameterAnnotations.length; i < parameterAnnotationsLength; i++) { Annotation[] annotations = parameterAnnotations[i]; for (Annotation annotation : annotations) { if (annotation.annotationType().isAssignableFrom(UseRSMapper.class)) { return i; } } } return -1; } private static RSMapper getCollectionORMapper(Type itemType) { RowMapperFactory rowMapperFactory = getRowMapperFactory(itemType); //noinspection unchecked return new CollectionRSMapper(rowMapperFactory); } public static RowMapperFactory getRowMapperFactory(Type itemType) { return getRowMapperFactory(itemType, 1); } public static RowMapperFactory getRowMapperFactory(Type itemType, int startIdx) { // see if return type is simple so that we should map just startIdx column RowMapperFactory scalarMapperFactory = getScalarMapper(itemType, startIdx, false); if (scalarMapperFactory != null) { return scalarMapperFactory; } if (itemType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) itemType; final Type rawType = parameterizedType.getRawType(); if (rawType instanceof Class && Map.class.isAssignableFrom((Class<?>) rawType)) { return new RowMapperFactory() { public RowMapper get() { return new MapRowMapper(); } }; } } if (itemType instanceof Class) { final Class itemClass = (Class) itemType; if (RowMapper.class.isAssignableFrom(itemClass)) { //noinspection unchecked return new SelfRowMapperFactory(itemClass); } if (Map.class.isAssignableFrom(itemClass)) { return new RowMapperFactory() { public RowMapper get() { return new MapRowMapper(); } }; } return new BeanRowMapperFactory(itemClass); } throw new DaoException("No mapping defined for type " + itemType); } public static RowMapperFactory getScalarMapper(final Type itemType, final int idx, boolean req) { if (itemType == String.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getString(idx); } }; } if (itemType == Integer.class || itemType == Integer.TYPE) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getInt(idx); } }; } if (itemType == URL.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getURL(idx); } }; } if (itemType == BigInteger.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { final BigDecimal res = rs.getBigDecimal(idx); return res == null ? null : res.toBigInteger(); } }; } if (itemType == BigDecimal.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBigDecimal(idx); } }; } if (itemType == InputStream.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBinaryStream(idx); } }; } if (itemType == Boolean.class || itemType == Boolean.TYPE) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBoolean(idx); } }; } if (itemType == Blob.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBlob(idx); } }; } if (itemType == java.sql.Date.class || itemType == java.util.Date.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getTimestamp(idx); } }; } if (itemType instanceof Class) { final Class itemClass = (Class) itemType; final ColumnMapper columnMapper = ColumnMapperRegistry.lookup(itemClass); if (columnMapper != null) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return columnMapper.map(rs, idx); } }; } final Converter converter = ConvertUtils.lookup(itemClass); if (converter != null) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { String s = rs.getString(idx); if (s == null) { return null; } return converter.convert(itemClass, s); } }; } if (Enum.class.isAssignableFrom((Class<?>) itemType)) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { String s = rs.getString(idx); if (s == null) { return null; } //noinspection unchecked return Enum.valueOf((Class<Enum>) itemType, s); } }; } } if (req) { throw new IllegalArgumentException("no mapping defined for " + itemType); } return null; } //psdo: merge this with index based scalar mapper public static RowMapperFactory getScalarRowMapperFactory(final Type itemType, final String name, boolean req) { if (itemType == String.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getString(name); } }; } if (itemType == Integer.class || itemType == Integer.TYPE) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getInt(name); } }; } if (itemType == URL.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getURL(name); } }; } if (itemType == BigInteger.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { final BigDecimal res = rs.getBigDecimal(name); return res == null ? null : res.toBigInteger(); } }; } if (itemType == BigDecimal.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBigDecimal(name); } }; } if (itemType == InputStream.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBinaryStream(name); } }; } if (itemType == Boolean.class || itemType == Boolean.TYPE) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBoolean(name); } }; } if (itemType == Blob.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getBlob(name); } }; } if (itemType == java.sql.Date.class || itemType == java.util.Date.class) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return rs.getTimestamp(name); } }; } if (itemType instanceof Class) { final Class itemClass = (Class) itemType; final ColumnMapper columnMapper = ColumnMapperRegistry.lookup(itemClass); if (columnMapper != null) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { return columnMapper.map(rs, name); } }; } final Converter converter = ConvertUtils.lookup(itemClass); if (converter != null) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { String s = rs.getString(name); if (s == null) { return null; } return converter.convert(itemClass, s); } }; } if (Enum.class.isAssignableFrom((Class<?>) itemType)) { return new ScalarRMF() { public Object map(ResultSet rs) throws SQLException { String s = rs.getString(name); if (s == null) { return null; } //noinspection unchecked return Enum.valueOf((Class<Enum>) itemType, s); } }; } } if (req) { throw new IllegalArgumentException("no mapping defined for " + itemType); } return null; } }