This patch allows the object get/set datatypes to be float, double, or
int (if possible). The current codebase requires get/set datatypes to
be BigDecimal.
Index: BasicRowProcessor.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons/dbutils/src/java/org/apache/commons/dbutils/BasicRowProcessor.java,v
retrieving revision 1.10
diff -r1.10 BasicRowProcessor.java
2c2,58
<  * Copyright 2002-2004 The Apache Software Foundation
---
>  * $Header: 
> /home/cvs/jakarta-commons/dbutils/src/java/org/apache/commons/dbutils/BasicRowProcessor.java,v
>  1.5 2003/11/11 00:53:19 dgraham Exp $
>  * $Revision: 1.5 $
>  * $Date: 2003/11/11 00:53:19 $
>  *
>  * ====================================================================
>  *
>  * The Apache Software License, Version 1.1
>  *
>  * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
>  * reserved.
>  *
>  * Redistribution and use in source and binary forms, with or without
>  * modification, are permitted provided that the following conditions
>  * are met:
>  *
>  * 1. Redistributions of source code must retain the above copyright
>  *    notice, this list of conditions and the following disclaimer.
>  *
>  * 2. Redistributions in binary form must reproduce the above copyright
>  *    notice, this list of conditions and the following disclaimer in
>  *    the documentation and/or other materials provided with the
>  *    distribution.
>  *
>  * 3. The end-user documentation included with the redistribution, if
>  *    any, must include the following acknowledgement:
>  *       "This product includes software developed by the
>  *        Apache Software Foundation (http://www.apache.org/)."
>  *    Alternately, this acknowledgement may appear in the software itself,
>  *    if and wherever such third-party acknowledgements normally appear.
>  *
>  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
>  *    Foundation" must not be used to endorse or promote products derived
>  *    from this software without prior written permission. For written
>  *    permission, please contact [EMAIL PROTECTED]
>  *
>  * 5. Products derived from this software may not be called "Apache"
>  *    nor may "Apache" appear in their names without prior written
>  *    permission of the Apache Software Foundation.
>  *
>  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>  * SUCH DAMAGE.
>  * ====================================================================
>  *
>  * This software consists of voluntary contributions made by many
>  * individuals on behalf of the Apache Software Foundation.  For more
>  * information on the Apache Software Foundation, please see
>  * <http://www.apache.org/>.
4,14d59
<  * 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.
15a61
> 
17a64,69
> import java.beans.BeanInfo;
> import java.beans.IntrospectionException;
> import java.beans.Introspector;
> import java.beans.PropertyDescriptor;
> import java.lang.reflect.InvocationTargetException;
> import java.lang.reflect.Method;
20a73
> import java.util.ArrayList;
24a78
> import java.math.BigDecimal;
28,32c82,83
<  * 
<  * <p>
<  * This class is thread-safe.
<  * </p>
<  * 
---
>  * This class is a thread-safe Singleton.
>  *
33a85,89
>  *
>  * @author Henri Yandell
>  * @author Juozas Baliuka
>  * @author David Graham
>  * @author Yoav Shapira
38,39c94,113
<      * The default BeanProcessor instance to use if not supplied in the
<      * constructor.
---
>      * Set a bean's primitive properties to these defaults when SQL NULL
>      * is returned.  These are the same as the defaults that ResultSet get*
>      * methods return in the event of a NULL column.
>      */
>     private static final Map primitiveDefaults = new HashMap();
> 
>     static {
>         primitiveDefaults.put(Integer.TYPE, new Integer(0));
>         primitiveDefaults.put(Short.TYPE, new Short((short) 0));
>         primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
>         primitiveDefaults.put(Float.TYPE, new Float(0));
>         primitiveDefaults.put(Double.TYPE, new Double(0));
>         primitiveDefaults.put(Long.TYPE, new Long(0));
>         primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
>         primitiveDefaults.put(Character.TYPE, new Character('\u0000'));
>     }
> 
>     /**
>      * Special array index that indicates there is no bean property that
>      * matches a column from a ResultSet.
41c115
<     private static final BeanProcessor defaultConvert = new BeanProcessor();
---
>     private static final int PROPERTY_NOT_FOUND = -1;
52,53d125
<      * @deprecated Create instances with the constructors instead.  This will 
<      * be removed after DbUtils 1.1.
60,76c132
<      * Use this to process beans.
<      */
<     private BeanProcessor convert = null;
< 
<     /**
<      * BasicRowProcessor constructor.  Bean processing defaults to a 
<      * BeanProcessor instance.
<      */
<     public BasicRowProcessor() {
<         this(defaultConvert);
<     }
<     
<     /**
<      * BasicRowProcessor constructor.
<      * @param convert The BeanProcessor to use when converting columns to 
<      * bean properties.
<      * @since DbUtils 1.1
---
>      * Protected constructor for BasicRowProcessor subclasses only.
78c134
<     public BasicRowProcessor(BeanProcessor convert) {
---
>     protected BasicRowProcessor() {
80d135
<         this.convert = convert;
85c140
<      * This implementation copies column values into the array in the same 
---
>      * This implementation copies column values into the array in the same
104,106c159,183
<      * Convert a <code>ResultSet</code> row into a JavaBean.  This 
<      * implementation delegates to a BeanProcessor instance.
<      * @see org.apache.commons.dbutils.BeanProcessor#toBean(java.sql.ResultSet, 
java.lang.Class) 
---
>      * Convert a <code>ResultSet</code> row into a JavaBean.  This
>      * implementation uses reflection and <code>BeanInfo</code> classes to
>      * match column names to bean property names.  Properties are matched to
>      * columns based on several factors:
>      * <br/>
>      * <ol>
>      *     <li>
>      *     The class has a writable property with the same name as a column.
>      *     The name comparison is case insensitive.
>      *     </li>
>      *
>      *     <li>
>      *     The property's set method parameter type matches the column
>      *     type. If the data types do not match, the setter will not be called.
>      *     </li>
>      * </ol>
>      *
>      * <p>
>      * Primitive bean properties are set to their defaults when SQL NULL is
>      * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
>      * and booleans are set to false.  Object bean properties are set to
>      * <code>null</code> when SQL NULL is returned.  This is the same behavior
>      * as the <code>ResultSet</code> get* methods.
>      * </p>
>      *
110c187,196
<         return this.convert.toBean(rs, type);
---
> 
>         PropertyDescriptor[] props = this.propertyDescriptors(type);
> 
>         ResultSetMetaData rsmd = rs.getMetaData();
> 
>         int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
> 
>         int cols = rsmd.getColumnCount();
> 
>         return this.createBean(rs, type, props, columnToProperty, cols);
114,116c200,224
<      * Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.  
<      * This implementation delegates to a BeanProcessor instance. 
<      * @see org.apache.commons.dbutils.BeanProcessor#toBeanList(java.sql.ResultSet, 
java.lang.Class)
---
>      * Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.
>      * This implementation uses reflection and <code>BeanInfo</code> classes to
>      * match column names to bean property names. Properties are matched to
>      * columns based on several factors:
>      * <br/>
>      * <ol>
>      *     <li>
>      *     The class has a writable property with the same name as a column.
>      *     The name comparison is case insensitive.
>      *     </li>
>      *
>      *     <li>
>      *     The property's set method parameter type matches the column
>      *     type. If the data types do not match, the setter will not be called.
>      *     </li>
>      * </ol>
>      *
>      * <p>
>      * Primitive bean properties are set to their defaults when SQL NULL is
>      * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
>      * and booleans are set to false.  Object bean properties are set to
>      * <code>null</code> when SQL NULL is returned.  This is the same behavior
>      * as the <code>ResultSet</code> get* methods.
>      * </p>
>      *
120c228,244
<         return this.convert.toBeanList(rs, type);
---
>         List results = new ArrayList();
> 
>         if (!rs.next()) {
>             return results;
>         }
> 
>         PropertyDescriptor[] props = this.propertyDescriptors(type);
>         ResultSetMetaData rsmd = rs.getMetaData();
>         int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
>         int cols = rsmd.getColumnCount();
> 
>         do {
>             results.add(this.createBean(rs, type, props, columnToProperty, cols));
> 
>         } while (rs.next());
> 
>         return results;
124c248,328
<      * Convert a <code>ResultSet</code> row into a <code>Map</code>.  This 
---
>      * Creates a new object and initializes its fields from the ResultSet.
>      *
>      * @param rs The result set
>      * @param type The bean type (the return type of the object)
>      * @param props The property descriptors
>      * @param columnToProperty The column indices in the result set
>      * @param cols The number of columns
>      * @return An initialized object.
>      * @throws SQLException If a database error occurs
>      */
>     private Object createBean(
>         ResultSet rs,
>         Class type,
>         PropertyDescriptor[] props,
>         int[] columnToProperty,
>         int cols)
>         throws SQLException {
> 
>         Object bean = this.newInstance(type);
> 
>         for (int i = 1; i <= cols; i++) {
> 
>             if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
>                 continue;
>             }
> 
>             Object value = rs.getObject(i);
> 
>             PropertyDescriptor prop = props[columnToProperty[i]];
>             Class propType = prop.getPropertyType();
> 
>             if (propType != null && value == null && propType.isPrimitive()) {
>                 value = primitiveDefaults.get(propType);
>             }
> 
>             this.callSetter(bean, prop, value);
>         }
> 
>         return bean;
>     }
> 
>     /**
>      * The positions in the returned array represent column numbers.  The values
>      * stored at each position represent the index in the PropertyDescriptor[]
>      * for the bean property that matches the column name.  If no bean property
>      * was found for a column, the position is set to PROPERTY_NOT_FOUND.
>      *
>      * @param rsmd The result set meta data containing column information
>      * @param props The bean property descriptors
>      * @return An int[] with column index to property index mappings.  The 0th
>      * element is meaningless as column indexing starts at 1.
>      *
>      * @throws SQLException If a database error occurs
>      */
>     private int[] mapColumnsToProperties(
>         ResultSetMetaData rsmd,
>         PropertyDescriptor[] props)
>         throws SQLException {
> 
>         int cols = rsmd.getColumnCount();
>         int columnToProperty[] = new int[cols + 1];
> 
>         for (int col = 1; col <= cols; col++) {
>             String columnName = rsmd.getColumnName(col);
>             for (int i = 0; i < props.length; i++) {
> 
>                 if (columnName.equalsIgnoreCase(props[i].getName())) {
>                     columnToProperty[col] = i;
>                     break;
> 
>                 } else {
>                     columnToProperty[col] = PROPERTY_NOT_FOUND;
>                 }
>             }
>         }
> 
>         return columnToProperty;
>     }
> 
>     /**
>      * Convert a <code>ResultSet</code> row into a <code>Map</code>.  This
126c330
<      * names as keys.  Calls to <code>map.get("COL")</code> and 
---
>      * names as keys.  Calls to <code>map.get("COL")</code> and
141c345,523
<     
---
> 
>     /**
>      * Calls the setter method on the target object for the given property.
>      * If no setter method exists for the property, this method does nothing.
>      * @param target The object to set the property on.
>      * @param prop The property to set.
>      * @param value The value to pass into the setter.
>      * @throws SQLException if an error occurs setting the property.
>      */
>     private void callSetter(
>         Object target,
>         PropertyDescriptor prop,
>         Object value)
>         throws SQLException {
> 
>         Method setter = prop.getWriteMethod();
> 
>         if (setter == null) {
>             return;
>         }
> 
>         Class[] params = setter.getParameterTypes();
>         try {
>             // Don't call setter if the value object isn't the right type
>             if (this.isCompatibleType(value, params[0])) {
>                 setter.invoke(target, new Object[] { value });
>             }
>             else
>             {
>                 //see if there are any conversions that can be made w/o resulting
>                 //in data loss...
>                 value = convertObject(value, params[0]);
>                 if (value != null)
>                 {
>                    setter.invoke(target, new Object[] { value });
>                 }
>             }
> 
>         } catch (IllegalArgumentException e) {
>             throw new SQLException(
>                 "Cannot set " + prop.getName() + ": " + e.getMessage());
> 
>         } catch (IllegalAccessException e) {
>             throw new SQLException(
>                 "Cannot set " + prop.getName() + ": " + e.getMessage());
> 
>         } catch (InvocationTargetException e) {
>             throw new SQLException(
>                 "Cannot set " + prop.getName() + ": " + e.getMessage());
>         }
>     }
> 
>     /**
>      * ResultSet.getObject() returns an Integer object for an INT column.  The
>      * setter method for the property might take an Integer or a primitive int.
>      * This method returns true if the value can be successfully passed into
>      * the setter method.  Remember, Method.invoke() handles the unwrapping
>      * of Integer into an int.
>      *
>      * @param value The value to be passed into the setter method.
>      * @param type The setter's parameter type.
>      * @return boolean True if the value is compatible.
>      */
>     private boolean isCompatibleType(Object value, Class type) {
>         // Do object check first, then primitives
>         if (value == null || type.isInstance(value)) {
>             return true;
> 
>         } else if (
>             type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
>             return true;
> 
>         } else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
>             return true;
> 
>         } else if (
>             type.equals(Double.TYPE) && Double.class.isInstance(value)) {
>             return true;
> 
>         } else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
>             return true;
> 
>         } else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
>             return true;
> 
>         } else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
>             return true;
> 
>         } else if (
>             type.equals(Character.TYPE) && Character.class.isInstance(value)) {
>             return true;
> 
>         } else if (
>             type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
>             return true;
> 
>         } else {
>             return false;
>         }
> 
>     }
> 
>     /**
>      * attempt to make a data type conversion from value to type
>      * @param value Object object to be converted
>      * @param type Class target converstion type
>      * @return Object resulting conversion, null if conversion was not possible
>      * @todo additional conversions?
>      */
>     private Object convertObject(Object value, Class type) {
>       Object converted = null;
>       if (value != null) {
> 
>         if (BigDecimal.class.isInstance(value))
>         {
>           //attempt conversion; only perform if dataloss can be avoided
>           BigDecimal bd = (BigDecimal)value;
>           if (type.equals(Integer.TYPE))
>           {
>             if (bd.scale() == 0) {
>               converted = new Integer(bd.intValue());
>             }
>           }
>           else if (type.equals(Float.TYPE))
>           {
>             converted = new Float(bd.floatValue());
>           }
>           else if (type.equals(Double.TYPE))
>           {
>             converted = new Double(bd.doubleValue());
>           }
>         }
>       }
>       return converted;
>     }
> 
>     /**
>      * Returns a new instance of the given Class.
>      *
>      * @param c The Class to create an object from.
>      * @return A newly created object of the Class.
>      * @throws SQLException if creation failed.
>      */
>     private Object newInstance(Class c) throws SQLException {
>         try {
>             return c.newInstance();
> 
>         } catch (InstantiationException e) {
>             throw new SQLException(
>                 "Cannot create " + c.getName() + ": " + e.getMessage());
> 
>         } catch (IllegalAccessException e) {
>             throw new SQLException(
>                 "Cannot create " + c.getName() + ": " + e.getMessage());
>         }
>     }
> 
>     /**
>      * Returns a PropertyDescriptor[] for the given Class.
>      *
>      * @param c The Class to retrieve PropertyDescriptors for.
>      * @return A PropertyDescriptor[] describing the Class.
>      * @throws SQLException if introspection failed.
>      */
>     private PropertyDescriptor[] propertyDescriptors(Class c)
>         throws SQLException {
>         // Introspector caches BeanInfo classes for better performance
>         BeanInfo beanInfo = null;
>         try {
>             beanInfo = Introspector.getBeanInfo(c);
> 
>         } catch (IntrospectionException e) {
>             throw new SQLException(
>                 "Bean introspection failed: " + e.getMessage());
>         }
> 
>         return beanInfo.getPropertyDescriptors();
>     }
> 
144,145c526,527
<      * lookups.  This is needed for the toMap() implementation because 
<      * databases don't consistenly handle the casing of column names. 
---
>      * lookups.  This is needed for the toMap() implementation because
>      * databases don't consistenly handle the casing of column names.
183c565
<          * @see java.util.Map#remove(java.lang.Object)
---
>          * @see java.util.Map#remove(java.lang.ObjecT)
189c571
<     
---
> 

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to