1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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: 557031 $ $Date: 2007-07-17 20:12:54 +0100 (Tue, 17 Jul 2007) $
37 */
38
39 abstract class JDBCDynaClass implements DynaClass, Serializable {
40
41
42
43 /***
44 * <p>Flag defining whether column names should be lower cased when
45 * converted to property names.</p>
46 */
47 protected boolean lowerCase = true;
48
49 /***
50 * <p>The set of dynamic properties that are part of this
51 * {@link DynaClass}.</p>
52 */
53 protected DynaProperty[] properties = null;
54
55 /***
56 * <p>The set of dynamic properties that are part of this
57 * {@link DynaClass}, keyed by the property name. Individual descriptor
58 * instances will be the same instances as those in the
59 * <code>properties</code> list.</p>
60 */
61 protected Map propertiesMap = new HashMap();
62
63 /***
64 * Cross Reference for column name --> dyna property name
65 * (needed when lowerCase option is true)
66 */
67 private Map columnNameXref;
68
69
70
71 /***
72 * <p>Return the name of this DynaClass (analogous to the
73 * <code>getName()</code> method of <code>java.lang.Class</code), which
74 * allows the same <code>DynaClass</code> implementation class to support
75 * different dynamic classes, with different sets of properties.</p>
76 */
77 public String getName() {
78
79 return (this.getClass().getName());
80
81 }
82
83 /***
84 * <p>Return a property descriptor for the specified property, if it
85 * exists; otherwise, return <code>null</code>.</p>
86 *
87 * @param name Name of the dynamic property for which a descriptor
88 * is requested
89 *
90 * @exception IllegalArgumentException if no property name is specified
91 */
92 public DynaProperty getDynaProperty(String name) {
93
94 if (name == null) {
95 throw new IllegalArgumentException("No property name specified");
96 }
97 return ((DynaProperty) propertiesMap.get(name));
98
99 }
100
101 /***
102 * <p>Return an array of <code>ProperyDescriptors</code> for the properties
103 * currently defined in this DynaClass. If no properties are defined, a
104 * zero-length array will be returned.</p>
105 */
106 public DynaProperty[] getDynaProperties() {
107
108 return (properties);
109
110 }
111
112 /***
113 * <p>Instantiate and return a new DynaBean instance, associated
114 * with this DynaClass. <strong>NOTE</strong> - This operation is not
115 * supported, and throws an exception.</p>
116 *
117 * @exception IllegalAccessException if the Class or the appropriate
118 * constructor is not accessible
119 * @exception InstantiationException if this Class represents an abstract
120 * class, an array class, a primitive type, or void; or if instantiation
121 * fails for some other reason
122 */
123 public DynaBean newInstance()
124 throws IllegalAccessException, InstantiationException {
125
126 throw new UnsupportedOperationException("newInstance() not supported");
127
128 }
129
130 /***
131 * <p>Loads and returns the <code>Class</code> of the given name.
132 * By default, a load from the thread context class loader is attempted.
133 * If there is no such class loader, the class loader used to load this
134 * class will be utilized.</p>
135 *
136 * @param className The name of the class to load
137 * @return The loaded class
138 * @exception SQLException if an exception was thrown trying to load
139 * the specified class
140 */
141 protected Class loadClass(String className) throws SQLException {
142
143 try {
144 ClassLoader cl = Thread.currentThread().getContextClassLoader();
145 if (cl == null) {
146 cl = this.getClass().getClassLoader();
147 }
148 return (cl.loadClass(className));
149 } catch (Exception e) {
150 throw new SQLException(
151 "Cannot load column class '" + className + "': " + e);
152 }
153
154 }
155
156 /***
157 * <p>Factory method to create a new DynaProperty for the given index
158 * into the result set metadata.</p>
159 *
160 * @param metadata is the result set metadata
161 * @param i is the column index in the metadata
162 * @return the newly created DynaProperty instance
163 * @throws SQLException If an error occurs accessing the SQL metadata
164 */
165 protected DynaProperty createDynaProperty(
166 ResultSetMetaData metadata,
167 int i)
168 throws SQLException {
169
170 String columnName = metadata.getColumnName(i);
171 String name = lowerCase ? columnName.toLowerCase() : columnName;
172 if (!name.equals(columnName)) {
173 if (columnNameXref == null) {
174 columnNameXref = new HashMap();
175 }
176 columnNameXref.put(name, columnName);
177 }
178 String className = null;
179 try {
180 int sqlType = metadata.getColumnType(i);
181 switch (sqlType) {
182 case java.sql.Types.DATE:
183 return new DynaProperty(name, java.sql.Date.class);
184 case java.sql.Types.TIMESTAMP:
185 return new DynaProperty(name, java.sql.Timestamp.class);
186 case java.sql.Types.TIME:
187 return new DynaProperty(name, java.sql.Time.class);
188 default:
189 className = metadata.getColumnClassName(i);
190 }
191 } catch (SQLException e) {
192
193
194 }
195
196
197
198 Class clazz = Object.class;
199 if (className != null) {
200 clazz = loadClass(className);
201 }
202 return new DynaProperty(name, clazz);
203
204 }
205
206 /***
207 * <p>Introspect the metadata associated with our result set, and populate
208 * the <code>properties</code> and <code>propertiesMap</code> instance
209 * variables.</p>
210 *
211 * @param resultSet The <code>resultSet</code> whose metadata is to
212 * be introspected
213 *
214 * @exception SQLException if an error is encountered processing the
215 * result set metadata
216 */
217 protected void introspect(ResultSet resultSet) throws SQLException {
218
219
220 ArrayList list = new ArrayList();
221 ResultSetMetaData metadata = resultSet.getMetaData();
222 int n = metadata.getColumnCount();
223 for (int i = 1; i <= n; i++) {
224 DynaProperty dynaProperty = createDynaProperty(metadata, i);
225 if (dynaProperty != null) {
226 list.add(dynaProperty);
227 }
228 }
229
230
231 properties =
232 (DynaProperty[]) list.toArray(new DynaProperty[list.size()]);
233 for (int i = 0; i < properties.length; i++) {
234 propertiesMap.put(properties[i].getName(), properties[i]);
235 }
236
237 }
238
239 /***
240 * Get a column value from a {@link ResultSet} for the specified name.
241 *
242 * @param resultSet The result set
243 * @param name The property name
244 * @return The value
245 * @throws SQLException if an error occurs
246 */
247 protected Object getObject(ResultSet resultSet, String name) throws SQLException {
248
249 DynaProperty property = getDynaProperty(name);
250 if (property == null) {
251 throw new IllegalArgumentException("Invalid name '" + name + "'");
252 }
253 String columnName = getColumnName(name);
254 Class type = property.getType();
255
256
257 if (type.equals(Date.class)) {
258 return resultSet.getDate(columnName);
259 }
260
261
262 if (type.equals(Timestamp.class)) {
263 return resultSet.getTimestamp(columnName);
264 }
265
266
267 if (type.equals(Time.class)) {
268 return resultSet.getTime(columnName);
269 }
270
271 return resultSet.getObject(columnName);
272 }
273
274 /***
275 * Get the table column name for the specified property name.
276 *
277 * @param name The property name
278 * @return The column name (which can be different if the <i>lowerCase</i>
279 * option is used).
280 */
281 protected String getColumnName(String name) {
282 if (columnNameXref != null && columnNameXref.containsKey(name)) {
283 return (String)columnNameXref.get(name);
284 } else {
285 return name;
286 }
287 }
288
289 }