001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019:
020: package org.apache.beehive.controls.system.jdbc;
021:
022: import org.apache.beehive.controls.api.ControlException;
023:
024: import java.sql.ResultSet;
025: import java.sql.ResultSetMetaData;
026: import java.sql.SQLException;
027: import java.util.Calendar;
028: import java.util.regex.Pattern;
029: import java.util.regex.Matcher;
030: import java.lang.reflect.Method;
031: import java.lang.reflect.Modifier;
032:
033: /**
034: * Abstract base class for all row mappers.
035: *
036: * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method.
037: * Supported RowMapper types include: HashMap, Map, Object, XmlObject. When a ResultSetMapper is ready to
038: * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the
039: * RowMapperFactory.
040: *
041: */
042: public abstract class RowMapper {
043:
044: private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)";
045: protected static final TypeMappingsFactory _tmf = TypeMappingsFactory
046: .getInstance();
047: protected static final Pattern _setterRegex = Pattern
048: .compile(SETTER_NAME_REGEX);
049:
050: /** ResultSet to map. */
051: protected final ResultSet _resultSet;
052:
053: /** Calendar instance for date/time mappings. */
054: protected final Calendar _cal;
055:
056: /** Class to map ResultSet Rows to. */
057: protected final Class<?> _returnTypeClass;
058:
059: /**
060: * Create a new RowMapper for the specified ResultSet and return type Class.
061: * @param resultSet ResultSet to map
062: * @param returnTypeClass Class to map ResultSet rows to.
063: * @param cal Calendar instance for date/time values.
064: */
065: protected RowMapper(ResultSet resultSet, Class<?> returnTypeClass,
066: Calendar cal) {
067: _resultSet = resultSet;
068: _returnTypeClass = returnTypeClass;
069: _cal = cal;
070: }
071:
072: /**
073: * Map a ResultSet row to the return type class
074: * @return An instance of class.
075: */
076: public abstract Object mapRowToReturnType();
077:
078: /**
079: * Build a String array of column names from the ResultSet.
080: * @return A String array containing the column names contained within the ResultSet.
081: * @throws SQLException on error
082: */
083: protected String[] getKeysFromResultSet() throws SQLException {
084:
085: String[] keys;
086: final ResultSetMetaData md = _resultSet.getMetaData();
087: final int columnCount = md.getColumnCount();
088:
089: keys = new String[columnCount + 1];
090: for (int i = 1; i <= columnCount; i++) {
091: keys[i] = md.getColumnName(i).toUpperCase();
092: }
093: return keys;
094: }
095:
096: /**
097: * Determine if the given method is a java bean setter method.
098: * @param method Method to check
099: * @return True if the method is a setter method.
100: */
101: protected boolean isSetterMethod(Method method) {
102: Matcher matcher = _setterRegex.matcher(method.getName());
103: if (matcher.matches()) {
104:
105: if (Modifier.isStatic(method.getModifiers()))
106: return false;
107: if (!Modifier.isPublic(method.getModifiers()))
108: return false;
109: if (!Void.TYPE.equals(method.getReturnType()))
110: return false;
111:
112: // method parameter checks
113: Class[] params = method.getParameterTypes();
114: if (params.length != 1)
115: return false;
116: if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf
117: .getTypeId(params[0]))
118: return false;
119:
120: return true;
121: }
122: return false;
123: }
124:
125: /**
126: * Extract a column value from the ResultSet and return it as resultType.
127: *
128: * @param index The column index of the value to extract from the ResultSet.
129: * @param resultType The return type. Defined in TypeMappingsFactory.
130: * @return The extracted value
131: * @throws java.sql.SQLException on error.
132: */
133: protected Object extractColumnValue(int index, int resultType)
134: throws SQLException {
135:
136: switch (resultType) {
137: case TypeMappingsFactory.TYPE_INT:
138: return new Integer(_resultSet.getInt(index));
139: case TypeMappingsFactory.TYPE_LONG:
140: return new Long(_resultSet.getLong(index));
141: case TypeMappingsFactory.TYPE_FLOAT:
142: return new Float(_resultSet.getFloat(index));
143: case TypeMappingsFactory.TYPE_DOUBLE:
144: return new Double(_resultSet.getDouble(index));
145: case TypeMappingsFactory.TYPE_BYTE:
146: return new Byte(_resultSet.getByte(index));
147: case TypeMappingsFactory.TYPE_SHORT:
148: return new Short(_resultSet.getShort(index));
149: case TypeMappingsFactory.TYPE_BOOLEAN:
150: return _resultSet.getBoolean(index) ? Boolean.TRUE
151: : Boolean.FALSE;
152: case TypeMappingsFactory.TYPE_INT_OBJ: {
153: int i = _resultSet.getInt(index);
154: return _resultSet.wasNull() ? null : new Integer(i);
155: }
156: case TypeMappingsFactory.TYPE_LONG_OBJ: {
157: long i = _resultSet.getLong(index);
158: return _resultSet.wasNull() ? null : new Long(i);
159: }
160: case TypeMappingsFactory.TYPE_FLOAT_OBJ: {
161: float i = _resultSet.getFloat(index);
162: return _resultSet.wasNull() ? null : new Float(i);
163: }
164: case TypeMappingsFactory.TYPE_DOUBLE_OBJ: {
165: double i = _resultSet.getDouble(index);
166: return _resultSet.wasNull() ? null : new Double(i);
167: }
168: case TypeMappingsFactory.TYPE_BYTE_OBJ: {
169: byte i = _resultSet.getByte(index);
170: return _resultSet.wasNull() ? null : new Byte(i);
171: }
172: case TypeMappingsFactory.TYPE_SHORT_OBJ: {
173: short i = _resultSet.getShort(index);
174: return _resultSet.wasNull() ? null : new Short(i);
175: }
176: case TypeMappingsFactory.TYPE_BOOLEAN_OBJ: {
177: boolean i = _resultSet.getBoolean(index);
178: return _resultSet.wasNull() ? null : (i ? Boolean.TRUE
179: : Boolean.FALSE);
180: }
181: case TypeMappingsFactory.TYPE_STRING:
182: case TypeMappingsFactory.TYPE_XMLBEAN_ENUM:
183: return _resultSet.getString(index);
184: case TypeMappingsFactory.TYPE_BIG_DECIMAL:
185: return _resultSet.getBigDecimal(index);
186: case TypeMappingsFactory.TYPE_BYTES:
187: return _resultSet.getBytes(index);
188: case TypeMappingsFactory.TYPE_TIMESTAMP: {
189: if (null == _cal)
190: return _resultSet.getTimestamp(index);
191: else
192: return _resultSet.getTimestamp(index, _cal);
193: }
194: case TypeMappingsFactory.TYPE_TIME: {
195: if (null == _cal)
196: return _resultSet.getTime(index);
197: else
198: return _resultSet.getTime(index, _cal);
199: }
200: case TypeMappingsFactory.TYPE_SQLDATE: {
201: if (null == _cal)
202: return _resultSet.getDate(index);
203: else
204: return _resultSet.getDate(index, _cal);
205: }
206: case TypeMappingsFactory.TYPE_DATE: {
207: // convert explicity to java.util.Date
208: // 12918 | knex does not return java.sql.Date properly from web service
209: java.sql.Timestamp ts = (null == _cal) ? _resultSet
210: .getTimestamp(index) : _resultSet.getTimestamp(
211: index, _cal);
212: if (null == ts)
213: return null;
214: return new java.util.Date(ts.getTime());
215: }
216: case TypeMappingsFactory.TYPE_CALENDAR: {
217: java.sql.Timestamp ts = (null == _cal) ? _resultSet
218: .getTimestamp(index) : _resultSet.getTimestamp(
219: index, _cal);
220: if (null == ts)
221: return null;
222: Calendar c = (null == _cal) ? Calendar.getInstance()
223: : (Calendar) _cal.clone();
224: c.setTimeInMillis(ts.getTime());
225: return c;
226: }
227: case TypeMappingsFactory.TYPE_REF:
228: return _resultSet.getRef(index);
229: case TypeMappingsFactory.TYPE_BLOB:
230: return _resultSet.getBlob(index);
231: case TypeMappingsFactory.TYPE_CLOB:
232: return _resultSet.getClob(index);
233: case TypeMappingsFactory.TYPE_ARRAY:
234: return _resultSet.getArray(index);
235: case TypeMappingsFactory.TYPE_READER:
236: case TypeMappingsFactory.TYPE_STREAM:
237: throw new ControlException(
238: "streaming return types are not supported by the JdbcControl; use ResultSet instead");
239: case TypeMappingsFactory.TYPE_STRUCT:
240: case TypeMappingsFactory.TYPE_UNKNOWN:
241: // JAVA_TYPE (could be any), or REF
242: return _resultSet.getObject(index);
243: default:
244: throw new ControlException(
245: "internal error: unknown type ID: "
246: + Integer.toString(resultType));
247: }
248: }
249: }
|