1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.beanutils;
  17. import java.io.Serializable;
  18. import java.sql.ResultSet;
  19. import java.sql.ResultSetMetaData;
  20. import java.sql.SQLException;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. /**
  25. * <p>Provides common logic for JDBC implementations of {@link DynaClass}.</p>
  26. *
  27. * @author Craig R. McClanahan
  28. * @author George Franciscus
  29. * @version $Revision: 1.4 $ $Date: 2004/02/28 13:18:33 $
  30. */
  31. abstract class JDBCDynaClass implements DynaClass, Serializable {
  32. // ----------------------------------------------------- Instance Variables
  33. /**
  34. * <p>Flag defining whether column names should be lower cased when
  35. * converted to property names.</p>
  36. */
  37. protected boolean lowerCase = true;
  38. /**
  39. * <p>The set of dynamic properties that are part of this
  40. * {@link DynaClass}.</p>
  41. */
  42. protected DynaProperty properties[] = null;
  43. /**
  44. * <p>The set of dynamic properties that are part of this
  45. * {@link DynaClass}, keyed by the property name. Individual descriptor
  46. * instances will be the same instances as those in the
  47. * <code>properties</code> list.</p>
  48. */
  49. protected Map propertiesMap = new HashMap();
  50. // ------------------------------------------------------ DynaClass Methods
  51. /**
  52. * <p>Return the name of this DynaClass (analogous to the
  53. * <code>getName()</code> method of <code>java.lang.Class</code), which
  54. * allows the same <code>DynaClass</code> implementation class to support
  55. * different dynamic classes, with different sets of properties.</p>
  56. */
  57. public String getName() {
  58. return (this.getClass().getName());
  59. }
  60. /**
  61. * <p>Return a property descriptor for the specified property, if it
  62. * exists; otherwise, return <code>null</code>.</p>
  63. *
  64. * @param name Name of the dynamic property for which a descriptor
  65. * is requested
  66. *
  67. * @exception IllegalArgumentException if no property name is specified
  68. */
  69. public DynaProperty getDynaProperty(String name) {
  70. if (name == null) {
  71. throw new IllegalArgumentException("No property name specified");
  72. }
  73. return ((DynaProperty) propertiesMap.get(name));
  74. }
  75. /**
  76. * <p>Return an array of <code>ProperyDescriptors</code> for the properties
  77. * currently defined in this DynaClass. If no properties are defined, a
  78. * zero-length array will be returned.</p>
  79. */
  80. public DynaProperty[] getDynaProperties() {
  81. return (properties);
  82. }
  83. /**
  84. * <p>Instantiate and return a new DynaBean instance, associated
  85. * with this DynaClass. <strong>NOTE</strong> - This operation is not
  86. * supported, and throws an exception.</p>
  87. *
  88. * @exception IllegalAccessException if the Class or the appropriate
  89. * constructor is not accessible
  90. * @exception InstantiationException if this Class represents an abstract
  91. * class, an array class, a primitive type, or void; or if instantiation
  92. * fails for some other reason
  93. */
  94. public DynaBean newInstance()
  95. throws IllegalAccessException, InstantiationException {
  96. throw new UnsupportedOperationException("newInstance() not supported");
  97. }
  98. /**
  99. * <p>Loads and returns the <code>Class</code> of the given name.
  100. * By default, a load from the thread context class loader is attempted.
  101. * If there is no such class loader, the class loader used to load this
  102. * class will be utilized.</p>
  103. *
  104. * @exception SQLException if an exception was thrown trying to load
  105. * the specified class
  106. */
  107. protected Class loadClass(String className) throws SQLException {
  108. try {
  109. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  110. if (cl == null) {
  111. cl = this.getClass().getClassLoader();
  112. }
  113. return (cl.loadClass(className));
  114. } catch (Exception e) {
  115. throw new SQLException(
  116. "Cannot load column class '" + className + "': " + e);
  117. }
  118. }
  119. /**
  120. * <p>Factory method to create a new DynaProperty for the given index
  121. * into the result set metadata.</p>
  122. *
  123. * @param metadata is the result set metadata
  124. * @param i is the column index in the metadata
  125. * @return the newly created DynaProperty instance
  126. */
  127. protected DynaProperty createDynaProperty(
  128. ResultSetMetaData metadata,
  129. int i)
  130. throws SQLException {
  131. String name = null;
  132. if (lowerCase) {
  133. name = metadata.getColumnName(i).toLowerCase();
  134. } else {
  135. name = metadata.getColumnName(i);
  136. }
  137. String className = null;
  138. try {
  139. className = metadata.getColumnClassName(i);
  140. } catch (SQLException e) {
  141. // this is a patch for HsqlDb to ignore exceptions
  142. // thrown by its metadata implementation
  143. }
  144. // Default to Object type if no class name could be retrieved
  145. // from the metadata
  146. Class clazz = Object.class;
  147. if (className != null) {
  148. clazz = loadClass(className);
  149. }
  150. return new DynaProperty(name, clazz);
  151. }
  152. /**
  153. * <p>Introspect the metadata associated with our result set, and populate
  154. * the <code>properties</code> and <code>propertiesMap</code> instance
  155. * variables.</p>
  156. *
  157. * @param resultSet The <code>resultSet</code> whose metadata is to
  158. * be introspected
  159. *
  160. * @exception SQLException if an error is encountered processing the
  161. * result set metadata
  162. */
  163. protected void introspect(ResultSet resultSet) throws SQLException {
  164. // Accumulate an ordered list of DynaProperties
  165. ArrayList list = new ArrayList();
  166. ResultSetMetaData metadata = resultSet.getMetaData();
  167. int n = metadata.getColumnCount();
  168. for (int i = 1; i <= n; i++) { // JDBC is one-relative!
  169. DynaProperty dynaProperty = createDynaProperty(metadata, i);
  170. if (dynaProperty != null) {
  171. list.add(dynaProperty);
  172. }
  173. }
  174. // Convert this list into the internal data structures we need
  175. properties =
  176. (DynaProperty[]) list.toArray(new DynaProperty[list.size()]);
  177. for (int i = 0; i < properties.length; i++) {
  178. propertiesMap.put(properties[i].getName(), properties[i]);
  179. }
  180. }
  181. }