/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. * * $Header:$ */ package com.moparisthebest.jdbc; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Abstract base class for all row mappers. * * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method. * Supported RowMapper types include: HashMap, Map, Object, XmlObject. When a ResultSetMapper is ready to * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the * RowMapperFactory. * */ public abstract class RowMapper { private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)"; protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance(); protected static final Pattern _setterRegex = Pattern.compile(SETTER_NAME_REGEX); /** ResultSet to map. */ protected final ResultSet _resultSet; /** Calendar instance for date/time mappings. */ protected final Calendar _cal; /** Class to map ResultSet Rows to. */ protected final Class _returnTypeClass; /** * Create a new RowMapper for the specified ResultSet and return type Class. * @param resultSet ResultSet to map * @param returnTypeClass Class to map ResultSet rows to. * @param cal Calendar instance for date/time values. */ protected RowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { _resultSet = resultSet; _returnTypeClass = returnTypeClass; _cal = cal; } /** * Map a ResultSet row to the return type class * @return An instance of class. */ public abstract Object mapRowToReturnType(); /** * Build a String array of column names from the ResultSet. * @return A String array containing the column names contained within the ResultSet. * @throws java.sql.SQLException on error */ protected String[] getKeysFromResultSet() throws SQLException { String[] keys; final ResultSetMetaData md = _resultSet.getMetaData(); final int columnCount = md.getColumnCount(); keys = new String[columnCount + 1]; for (int i = 1; i <= columnCount; i++) { keys[i] = md.getColumnName(i).toUpperCase(); } return keys; } /** * Determine if the given method is a java bean setter method. * @param method Method to check * @return True if the method is a setter method. */ protected boolean isSetterMethod(Method method) { Matcher matcher = _setterRegex.matcher(method.getName()); if (matcher.matches()) { if (Modifier.isStatic(method.getModifiers())) return false; if (!Modifier.isPublic(method.getModifiers())) return false; if (!Void.TYPE.equals(method.getReturnType())) return false; // method parameter checks Class[] params = method.getParameterTypes(); if (params.length != 1) return false; if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf.getTypeId(params[0])) return false; return true; } return false; } /** * Extract a column value from the ResultSet and return it as resultType. * * @param index The column index of the value to extract from the ResultSet. * @param resultType The return type. Defined in TypeMappingsFactory. * @return The extracted value * @throws java.sql.SQLException on error. */ protected Object extractColumnValue(int index, int resultType) throws SQLException { switch (resultType) { case TypeMappingsFactory.TYPE_INT: return new Integer(_resultSet.getInt(index)); case TypeMappingsFactory.TYPE_LONG: return new Long(_resultSet.getLong(index)); case TypeMappingsFactory.TYPE_FLOAT: return new Float(_resultSet.getFloat(index)); case TypeMappingsFactory.TYPE_DOUBLE: return new Double(_resultSet.getDouble(index)); case TypeMappingsFactory.TYPE_BYTE: return new Byte(_resultSet.getByte(index)); case TypeMappingsFactory.TYPE_SHORT: return new Short(_resultSet.getShort(index)); case TypeMappingsFactory.TYPE_BOOLEAN: return _resultSet.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE; case TypeMappingsFactory.TYPE_INT_OBJ: { int i = _resultSet.getInt(index); return _resultSet.wasNull() ? null : new Integer(i); } case TypeMappingsFactory.TYPE_LONG_OBJ: { long i = _resultSet.getLong(index); return _resultSet.wasNull() ? null : new Long(i); } case TypeMappingsFactory.TYPE_FLOAT_OBJ: { float i = _resultSet.getFloat(index); return _resultSet.wasNull() ? null : new Float(i); } case TypeMappingsFactory.TYPE_DOUBLE_OBJ: { double i = _resultSet.getDouble(index); return _resultSet.wasNull() ? null : new Double(i); } case TypeMappingsFactory.TYPE_BYTE_OBJ: { byte i = _resultSet.getByte(index); return _resultSet.wasNull() ? null : new Byte(i); } case TypeMappingsFactory.TYPE_SHORT_OBJ: { short i = _resultSet.getShort(index); return _resultSet.wasNull() ? null : new Short(i); } case TypeMappingsFactory.TYPE_BOOLEAN_OBJ: { boolean i = _resultSet.getBoolean(index); return _resultSet.wasNull() ? null : (i ? Boolean.TRUE : Boolean.FALSE); } case TypeMappingsFactory.TYPE_STRING: case TypeMappingsFactory.TYPE_XMLBEAN_ENUM: return _resultSet.getString(index); case TypeMappingsFactory.TYPE_BIG_DECIMAL: return _resultSet.getBigDecimal(index); case TypeMappingsFactory.TYPE_BYTES: return _resultSet.getBytes(index); case TypeMappingsFactory.TYPE_TIMESTAMP: { if (null == _cal) return _resultSet.getTimestamp(index); else return _resultSet.getTimestamp(index, _cal); } case TypeMappingsFactory.TYPE_TIME: { if (null == _cal) return _resultSet.getTime(index); else return _resultSet.getTime(index, _cal); } case TypeMappingsFactory.TYPE_SQLDATE: { if (null == _cal) return _resultSet.getDate(index); else return _resultSet.getDate(index, _cal); } case TypeMappingsFactory.TYPE_DATE: { // convert explicity to java.util.Date // 12918 | knex does not return java.sql.Date properly from web service java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); if (null == ts) return null; return new java.util.Date(ts.getTime()); } case TypeMappingsFactory.TYPE_CALENDAR: { java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); if (null == ts) return null; Calendar c = (null == _cal) ? Calendar.getInstance() : (Calendar) _cal.clone(); c.setTimeInMillis(ts.getTime()); return c; } case TypeMappingsFactory.TYPE_REF: return _resultSet.getRef(index); case TypeMappingsFactory.TYPE_BLOB: return _resultSet.getBlob(index); case TypeMappingsFactory.TYPE_CLOB: return _resultSet.getClob(index); case TypeMappingsFactory.TYPE_ARRAY: return _resultSet.getArray(index); case TypeMappingsFactory.TYPE_READER: case TypeMappingsFactory.TYPE_STREAM: throw new MapperException("streaming return types are not supported by the JdbcControl; use ResultSet instead"); case TypeMappingsFactory.TYPE_STRUCT: case TypeMappingsFactory.TYPE_UNKNOWN: // JAVA_TYPE (could be any), or REF return _resultSet.getObject(index); default: throw new MapperException("internal error: unknown type ID: " + Integer.toString(resultType)); } } }