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:
018: package org.apache.commons.beanutils;
019:
020: import java.io.Serializable;
021: import java.sql.Date;
022: import java.sql.ResultSet;
023: import java.sql.ResultSetMetaData;
024: import java.sql.SQLException;
025: import java.sql.Time;
026: import java.sql.Timestamp;
027: import java.util.ArrayList;
028: import java.util.HashMap;
029: import java.util.Map;
030:
031: /**
032: * <p>Provides common logic for JDBC implementations of {@link DynaClass}.</p>
033: *
034: * @author Craig R. McClanahan
035: * @author George Franciscus
036: * @version $Revision: 557031 $ $Date: 2007-07-17 20:12:54 +0100 (Tue, 17 Jul 2007) $
037: */
038:
039: abstract class JDBCDynaClass implements DynaClass, Serializable {
040:
041: // ----------------------------------------------------- Instance Variables
042:
043: /**
044: * <p>Flag defining whether column names should be lower cased when
045: * converted to property names.</p>
046: */
047: protected boolean lowerCase = true;
048:
049: /**
050: * <p>The set of dynamic properties that are part of this
051: * {@link DynaClass}.</p>
052: */
053: protected DynaProperty[] properties = null;
054:
055: /**
056: * <p>The set of dynamic properties that are part of this
057: * {@link DynaClass}, keyed by the property name. Individual descriptor
058: * instances will be the same instances as those in the
059: * <code>properties</code> list.</p>
060: */
061: protected Map propertiesMap = new HashMap();
062:
063: /**
064: * Cross Reference for column name --> dyna property name
065: * (needed when lowerCase option is true)
066: */
067: private Map columnNameXref;
068:
069: // ------------------------------------------------------ DynaClass Methods
070:
071: /**
072: * <p>Return the name of this DynaClass (analogous to the
073: * <code>getName()</code> method of <code>java.lang.Class</code), which
074: * allows the same <code>DynaClass</code> implementation class to support
075: * different dynamic classes, with different sets of properties.</p>
076: */
077: public String getName() {
078:
079: return (this .getClass().getName());
080:
081: }
082:
083: /**
084: * <p>Return a property descriptor for the specified property, if it
085: * exists; otherwise, return <code>null</code>.</p>
086: *
087: * @param name Name of the dynamic property for which a descriptor
088: * is requested
089: *
090: * @exception IllegalArgumentException if no property name is specified
091: */
092: public DynaProperty getDynaProperty(String name) {
093:
094: if (name == null) {
095: throw new IllegalArgumentException(
096: "No property name specified");
097: }
098: return ((DynaProperty) propertiesMap.get(name));
099:
100: }
101:
102: /**
103: * <p>Return an array of <code>ProperyDescriptors</code> for the properties
104: * currently defined in this DynaClass. If no properties are defined, a
105: * zero-length array will be returned.</p>
106: */
107: public DynaProperty[] getDynaProperties() {
108:
109: return (properties);
110:
111: }
112:
113: /**
114: * <p>Instantiate and return a new DynaBean instance, associated
115: * with this DynaClass. <strong>NOTE</strong> - This operation is not
116: * supported, and throws an exception.</p>
117: *
118: * @exception IllegalAccessException if the Class or the appropriate
119: * constructor is not accessible
120: * @exception InstantiationException if this Class represents an abstract
121: * class, an array class, a primitive type, or void; or if instantiation
122: * fails for some other reason
123: */
124: public DynaBean newInstance() throws IllegalAccessException,
125: InstantiationException {
126:
127: throw new UnsupportedOperationException(
128: "newInstance() not supported");
129:
130: }
131:
132: /**
133: * <p>Loads and returns the <code>Class</code> of the given name.
134: * By default, a load from the thread context class loader is attempted.
135: * If there is no such class loader, the class loader used to load this
136: * class will be utilized.</p>
137: *
138: * @param className The name of the class to load
139: * @return The loaded class
140: * @exception SQLException if an exception was thrown trying to load
141: * the specified class
142: */
143: protected Class loadClass(String className) throws SQLException {
144:
145: try {
146: ClassLoader cl = Thread.currentThread()
147: .getContextClassLoader();
148: if (cl == null) {
149: cl = this .getClass().getClassLoader();
150: }
151: return (cl.loadClass(className));
152: } catch (Exception e) {
153: throw new SQLException("Cannot load column class '"
154: + className + "': " + e);
155: }
156:
157: }
158:
159: /**
160: * <p>Factory method to create a new DynaProperty for the given index
161: * into the result set metadata.</p>
162: *
163: * @param metadata is the result set metadata
164: * @param i is the column index in the metadata
165: * @return the newly created DynaProperty instance
166: * @throws SQLException If an error occurs accessing the SQL metadata
167: */
168: protected DynaProperty createDynaProperty(
169: ResultSetMetaData metadata, int i) throws SQLException {
170:
171: String columnName = metadata.getColumnName(i);
172: String name = lowerCase ? columnName.toLowerCase() : columnName;
173: if (!name.equals(columnName)) {
174: if (columnNameXref == null) {
175: columnNameXref = new HashMap();
176: }
177: columnNameXref.put(name, columnName);
178: }
179: String className = null;
180: try {
181: int sqlType = metadata.getColumnType(i);
182: switch (sqlType) {
183: case java.sql.Types.DATE:
184: return new DynaProperty(name, java.sql.Date.class);
185: case java.sql.Types.TIMESTAMP:
186: return new DynaProperty(name, java.sql.Timestamp.class);
187: case java.sql.Types.TIME:
188: return new DynaProperty(name, java.sql.Time.class);
189: default:
190: className = metadata.getColumnClassName(i);
191: }
192: } catch (SQLException e) {
193: // this is a patch for HsqlDb to ignore exceptions
194: // thrown by its metadata implementation
195: }
196:
197: // Default to Object type if no class name could be retrieved
198: // from the metadata
199: Class clazz = Object.class;
200: if (className != null) {
201: clazz = loadClass(className);
202: }
203: return new DynaProperty(name, clazz);
204:
205: }
206:
207: /**
208: * <p>Introspect the metadata associated with our result set, and populate
209: * the <code>properties</code> and <code>propertiesMap</code> instance
210: * variables.</p>
211: *
212: * @param resultSet The <code>resultSet</code> whose metadata is to
213: * be introspected
214: *
215: * @exception SQLException if an error is encountered processing the
216: * result set metadata
217: */
218: protected void introspect(ResultSet resultSet) throws SQLException {
219:
220: // Accumulate an ordered list of DynaProperties
221: ArrayList list = new ArrayList();
222: ResultSetMetaData metadata = resultSet.getMetaData();
223: int n = metadata.getColumnCount();
224: for (int i = 1; i <= n; i++) { // JDBC is one-relative!
225: DynaProperty dynaProperty = createDynaProperty(metadata, i);
226: if (dynaProperty != null) {
227: list.add(dynaProperty);
228: }
229: }
230:
231: // Convert this list into the internal data structures we need
232: properties = (DynaProperty[]) list
233: .toArray(new DynaProperty[list.size()]);
234: for (int i = 0; i < properties.length; i++) {
235: propertiesMap.put(properties[i].getName(), properties[i]);
236: }
237:
238: }
239:
240: /**
241: * Get a column value from a {@link ResultSet} for the specified name.
242: *
243: * @param resultSet The result set
244: * @param name The property name
245: * @return The value
246: * @throws SQLException if an error occurs
247: */
248: protected Object getObject(ResultSet resultSet, String name)
249: throws SQLException {
250:
251: DynaProperty property = getDynaProperty(name);
252: if (property == null) {
253: throw new IllegalArgumentException("Invalid name '" + name
254: + "'");
255: }
256: String columnName = getColumnName(name);
257: Class type = property.getType();
258:
259: // java.sql.Date
260: if (type.equals(Date.class)) {
261: return resultSet.getDate(columnName);
262: }
263:
264: // java.sql.Timestamp
265: if (type.equals(Timestamp.class)) {
266: return resultSet.getTimestamp(columnName);
267: }
268:
269: // java.sql.Time
270: if (type.equals(Time.class)) {
271: return resultSet.getTime(columnName);
272: }
273:
274: return resultSet.getObject(columnName);
275: }
276:
277: /**
278: * Get the table column name for the specified property name.
279: *
280: * @param name The property name
281: * @return The column name (which can be different if the <i>lowerCase</i>
282: * option is used).
283: */
284: protected String getColumnName(String name) {
285: if (columnNameXref != null && columnNameXref.containsKey(name)) {
286: return (String) columnNameXref.get(name);
287: } else {
288: return name;
289: }
290: }
291:
292: }
|