| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| JDBCDynaClass |
|
| 4.5;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 | } |