Coverage Report - org.apache.commons.beanutils.JDBCDynaClass
 
Classes in this File Line Coverage Branch Coverage Complexity
JDBCDynaClass
91%
61/67
77%
31/40
4.5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.beanutils;
 19  
 
 20  
 import java.io.Serializable;
 21  
 import java.sql.Date;
 22  
 import java.sql.ResultSet;
 23  
 import java.sql.ResultSetMetaData;
 24  
 import java.sql.SQLException;
 25  
 import java.sql.Time;
 26  
 import java.sql.Timestamp;
 27  
 import java.util.ArrayList;
 28  
 import java.util.HashMap;
 29  
 import java.util.Map;
 30  
 
 31  
 /**
 32  
  * <p>Provides common logic for JDBC implementations of {@link DynaClass}.</p>
 33  
  *
 34  
  * @author   Craig R. McClanahan
 35  
  * @author   George Franciscus
 36  
  * @version $Revision: 926529 $ $Date: 2010-03-23 07:44:24 -0400 (Tue, 23 Mar 2010) $
 37  
  */
 38  
 
 39  20
 abstract class JDBCDynaClass implements DynaClass, Serializable {
 40  
 
 41  
     // ----------------------------------------------------- Instance Variables
 42  
 
 43  
     /**
 44  
      * <p>Flag defining whether column names should be lower cased when
 45  
      * converted to property names.</p>
 46  
      */
 47  20
     protected boolean lowerCase = true;
 48  
 
 49  
     /**
 50  
      * <p>Flag defining whether column names or labels should be used.
 51  
      */
 52  
     private boolean useColumnLabel;
 53  
 
 54  
     /**
 55  
      * <p>The set of dynamic properties that are part of this
 56  
      * {@link DynaClass}.</p>
 57  
      */
 58  20
     protected DynaProperty[] properties = null;
 59  
 
 60  
     /**
 61  
      * <p>The set of dynamic properties that are part of this
 62  
      * {@link DynaClass}, keyed by the property name.  Individual descriptor
 63  
      * instances will be the same instances as those in the
 64  
      * <code>properties</code> list.</p>
 65  
      */
 66  20
     protected Map propertiesMap = new HashMap();
 67  
 
 68  
     /**
 69  
      * Cross Reference for column name --> dyna property name
 70  
      * (needed when lowerCase option is true)
 71  
      */
 72  
     private Map columnNameXref;
 73  
 
 74  
     // ------------------------------------------------------ DynaClass Methods
 75  
 
 76  
     /**
 77  
      * <p>Return the name of this DynaClass (analogous to the
 78  
      * <code>getName()</code> method of <code>java.lang.Class</code), which
 79  
      * allows the same <code>DynaClass</code> implementation class to support
 80  
      * different dynamic classes, with different sets of properties.</p>
 81  
      */
 82  
     public String getName() {
 83  
 
 84  2
         return (this.getClass().getName());
 85  
 
 86  
     }
 87  
 
 88  
     /**
 89  
      * <p>Return a property descriptor for the specified property, if it
 90  
      * exists; otherwise, return <code>null</code>.</p>
 91  
      *
 92  
      * @param name Name of the dynamic property for which a descriptor
 93  
      *  is requested
 94  
      *
 95  
      * @exception IllegalArgumentException if no property name is specified
 96  
      */
 97  
     public DynaProperty getDynaProperty(String name) {
 98  
 
 99  1538
         if (name == null) {
 100  2
             throw new IllegalArgumentException("No property name specified");
 101  
         }
 102  1536
         return ((DynaProperty) propertiesMap.get(name));
 103  
 
 104  
     }
 105  
 
 106  
     /**
 107  
      * <p>Return an array of <code>ProperyDescriptors</code> for the properties
 108  
      * currently defined in this DynaClass.  If no properties are defined, a
 109  
      * zero-length array will be returned.</p>
 110  
      */
 111  
     public DynaProperty[] getDynaProperties() {
 112  
 
 113  2
         return (properties);
 114  
 
 115  
     }
 116  
 
 117  
     /**
 118  
      * <p>Instantiate and return a new DynaBean instance, associated
 119  
      * with this DynaClass.  <strong>NOTE</strong> - This operation is not
 120  
      * supported, and throws an exception.</p>
 121  
      *
 122  
      * @exception IllegalAccessException if the Class or the appropriate
 123  
      *  constructor is not accessible
 124  
      * @exception InstantiationException if this Class represents an abstract
 125  
      *  class, an array class, a primitive type, or void; or if instantiation
 126  
      *  fails for some other reason
 127  
      */
 128  
     public DynaBean newInstance()
 129  
             throws IllegalAccessException, InstantiationException {
 130  
 
 131  2
         throw new UnsupportedOperationException("newInstance() not supported");
 132  
 
 133  
     }
 134  
 
 135  
     /**
 136  
      * Set whether the column label or name should be used for the property name.
 137  
      *
 138  
      * @param useColumnLabel true if the column label should be used, otherwise false
 139  
      */
 140  
     public void setUseColumnLabel(boolean useColumnLabel) {
 141  20
         this.useColumnLabel = useColumnLabel;
 142  20
     }
 143  
 
 144  
     /**
 145  
      * <p>Loads and returns the <code>Class</code> of the given name.
 146  
      * By default, a load from the thread context class loader is attempted.
 147  
      * If there is no such class loader, the class loader used to load this
 148  
      * class will be utilized.</p>
 149  
      *
 150  
      * @param className The name of the class to load
 151  
      * @return The loaded class
 152  
      * @exception SQLException if an exception was thrown trying to load
 153  
      *  the specified class
 154  
      */
 155  
     protected Class loadClass(String className) throws SQLException {
 156  
 
 157  
         try {
 158  120
             ClassLoader cl = Thread.currentThread().getContextClassLoader();
 159  120
             if (cl == null) {
 160  0
                     cl = this.getClass().getClassLoader();
 161  
             }
 162  
             // use Class.forName() - see BEANUTILS-327
 163  120
             return Class.forName(className, false, cl);
 164  0
         } catch (Exception e) {
 165  0
             throw new SQLException(
 166  
                     "Cannot load column class '" + className + "': " + e);
 167  
         }
 168  
 
 169  
     }
 170  
 
 171  
     /**
 172  
      * <p>Factory method to create a new DynaProperty for the given index
 173  
      * into the result set metadata.</p>
 174  
      * 
 175  
      * @param metadata is the result set metadata
 176  
      * @param i is the column index in the metadata
 177  
      * @return the newly created DynaProperty instance
 178  
      * @throws SQLException If an error occurs accessing the SQL metadata
 179  
      */
 180  
     protected DynaProperty createDynaProperty(
 181  
                                     ResultSetMetaData metadata,
 182  
                                     int i)
 183  
                                     throws SQLException {
 184  
 
 185  260
         String columnName = null;
 186  260
         if (useColumnLabel) {
 187  0
             columnName = metadata.getColumnLabel(i);
 188  
         }
 189  260
         if (columnName == null || columnName.trim().length() == 0) {
 190  260
             columnName = metadata.getColumnName(i);
 191  
         }
 192  260
         String name = lowerCase ? columnName.toLowerCase() : columnName;
 193  260
         if (!name.equals(columnName)) {
 194  234
             if (columnNameXref == null) {
 195  18
                 columnNameXref = new HashMap();
 196  
             }
 197  234
             columnNameXref.put(name, columnName);
 198  
         }
 199  260
         String className = null;
 200  
         try {
 201  260
             int sqlType = metadata.getColumnType(i);
 202  260
             switch (sqlType) {
 203  
                 case java.sql.Types.DATE:
 204  20
                     return new DynaProperty(name, java.sql.Date.class);
 205  
                 case java.sql.Types.TIMESTAMP:
 206  20
                     return new DynaProperty(name, java.sql.Timestamp.class);
 207  
                 case java.sql.Types.TIME:
 208  20
                     return new DynaProperty(name, java.sql.Time.class);
 209  
                 default:
 210  200
                     className = metadata.getColumnClassName(i);
 211  
             }
 212  0
         } catch (SQLException e) {
 213  
             // this is a patch for HsqlDb to ignore exceptions
 214  
             // thrown by its metadata implementation
 215  200
         }
 216  
 
 217  
         // Default to Object type if no class name could be retrieved
 218  
         // from the metadata
 219  200
         Class clazz = Object.class;
 220  200
         if (className != null) {
 221  200
             clazz = loadClass(className);
 222  
         }
 223  200
         return new DynaProperty(name, clazz);
 224  
 
 225  
     }
 226  
 
 227  
     /**
 228  
      * <p>Introspect the metadata associated with our result set, and populate
 229  
      * the <code>properties</code> and <code>propertiesMap</code> instance
 230  
      * variables.</p>
 231  
      *
 232  
      * @param resultSet The <code>resultSet</code> whose metadata is to
 233  
      *  be introspected
 234  
      *
 235  
      * @exception SQLException if an error is encountered processing the
 236  
      *  result set metadata
 237  
      */
 238  
     protected void introspect(ResultSet resultSet) throws SQLException {
 239  
 
 240  
         // Accumulate an ordered list of DynaProperties
 241  20
         ArrayList list = new ArrayList();
 242  20
         ResultSetMetaData metadata = resultSet.getMetaData();
 243  20
         int n = metadata.getColumnCount();
 244  280
         for (int i = 1; i <= n; i++) { // JDBC is one-relative!
 245  260
             DynaProperty dynaProperty = createDynaProperty(metadata, i);
 246  260
             if (dynaProperty != null) {
 247  260
                     list.add(dynaProperty);
 248  
             }
 249  
         }
 250  
 
 251  
         // Convert this list into the internal data structures we need
 252  20
         properties =
 253  
             (DynaProperty[]) list.toArray(new DynaProperty[list.size()]);
 254  280
         for (int i = 0; i < properties.length; i++) {
 255  260
             propertiesMap.put(properties[i].getName(), properties[i]);
 256  
         }
 257  
 
 258  20
     }
 259  
 
 260  
     /**
 261  
      * Get a column value from a {@link ResultSet} for the specified name.
 262  
      *
 263  
      * @param resultSet The result set
 264  
      * @param name The property name
 265  
      * @return The value
 266  
      * @throws SQLException if an error occurs
 267  
      */
 268  
     protected Object getObject(ResultSet resultSet, String name) throws SQLException {
 269  
 
 270  762
         DynaProperty property = getDynaProperty(name);
 271  762
         if (property == null) {
 272  0
             throw new IllegalArgumentException("Invalid name '" + name + "'");
 273  
         }
 274  762
         String columnName = getColumnName(name);
 275  762
         Class type = property.getType();
 276  
 
 277  
         // java.sql.Date
 278  762
         if (type.equals(Date.class)) {
 279  58
             return resultSet.getDate(columnName);
 280  
         }
 281  
 
 282  
         // java.sql.Timestamp
 283  704
         if (type.equals(Timestamp.class)) {
 284  58
             return resultSet.getTimestamp(columnName);
 285  
         }
 286  
 
 287  
         // java.sql.Time
 288  646
         if (type.equals(Time.class)) {
 289  58
             return resultSet.getTime(columnName);
 290  
         }
 291  
 
 292  588
         return resultSet.getObject(columnName);
 293  
     }
 294  
 
 295  
     /**
 296  
      * Get the table column name for the specified property name.
 297  
      * 
 298  
      * @param name The property name
 299  
      * @return The column name (which can be different if the <i>lowerCase</i>
 300  
      * option is used).
 301  
      */
 302  
     protected String getColumnName(String name) {
 303  762
         if (columnNameXref != null && columnNameXref.containsKey(name)) {
 304  693
             return (String)columnNameXref.get(name);
 305  
         } else {
 306  69
             return name;
 307  
         }
 308  
     }
 309  
 
 310  
 }