1. /*
  2. * Copyright 1999-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.jxpath;
  17. import java.util.Date;
  18. import java.util.Map;
  19. import java.util.HashMap;
  20. /**
  21. * JXPathIntrospector maintains a registry of {@link JXPathBeanInfo
  22. * JXPathBeanInfo} objects for Java classes.
  23. *
  24. * @author Dmitri Plotnikov
  25. * @version $Revision: 1.10 $ $Date: 2004/05/08 15:10:05 $
  26. */
  27. public class JXPathIntrospector {
  28. private static HashMap byClass = new HashMap();
  29. private static HashMap byInterface = new HashMap();
  30. static {
  31. registerAtomicClass(Class.class);
  32. registerAtomicClass(Boolean.TYPE);
  33. registerAtomicClass(Boolean.class);
  34. registerAtomicClass(Byte.TYPE);
  35. registerAtomicClass(Byte.class);
  36. registerAtomicClass(Character.TYPE);
  37. registerAtomicClass(Character.class);
  38. registerAtomicClass(Short.TYPE);
  39. registerAtomicClass(Short.class);
  40. registerAtomicClass(Integer.TYPE);
  41. registerAtomicClass(Integer.class);
  42. registerAtomicClass(Long.TYPE);
  43. registerAtomicClass(Long.class);
  44. registerAtomicClass(Float.TYPE);
  45. registerAtomicClass(Float.class);
  46. registerAtomicClass(Double.TYPE);
  47. registerAtomicClass(Double.class);
  48. registerAtomicClass(String.class);
  49. registerAtomicClass(Date.class);
  50. registerAtomicClass(java.sql.Date.class);
  51. registerAtomicClass(java.sql.Time.class);
  52. registerAtomicClass(java.sql.Timestamp.class);
  53. registerDynamicClass(Map.class, MapDynamicPropertyHandler.class);
  54. }
  55. /**
  56. * Automatically creates and registers a JXPathBeanInfo object
  57. * for the specified class. That object returns true to isAtomic().
  58. */
  59. public static void registerAtomicClass(Class beanClass) {
  60. byClass.put(beanClass, new JXPathBasicBeanInfo(beanClass, true));
  61. }
  62. /**
  63. * Automatically creates and registers a JXPathBeanInfo object
  64. * for the specified class. That object returns true to isDynamic().
  65. */
  66. public static void registerDynamicClass(
  67. Class beanClass,
  68. Class dynamicPropertyHandlerClass)
  69. {
  70. JXPathBasicBeanInfo bi =
  71. new JXPathBasicBeanInfo(beanClass, dynamicPropertyHandlerClass);
  72. if (beanClass.isInterface()) {
  73. byInterface.put(beanClass, bi);
  74. }
  75. else {
  76. byClass.put(beanClass, bi);
  77. }
  78. }
  79. /**
  80. * Creates and registers a JXPathBeanInfo object for the supplied class. If
  81. * the class has already been registered, returns the registered
  82. * JXPathBeanInfo object.
  83. * <p>
  84. * The process of creation of JXPathBeanInfo is as follows:
  85. * <ul>
  86. * <li>If class named <code><beanClass>XBeanInfo</code> exists,
  87. * an instance of that class is allocated.
  88. * <li>Otherwise, an instance of {@link JXPathBasicBeanInfo
  89. * JXPathBasicBeanInfo} is allocated.
  90. * </ul>
  91. */
  92. public static JXPathBeanInfo getBeanInfo(Class beanClass) {
  93. JXPathBeanInfo beanInfo = (JXPathBeanInfo) byClass.get(beanClass);
  94. if (beanInfo == null) {
  95. beanInfo = findDynamicBeanInfo(beanClass);
  96. if (beanInfo == null) {
  97. beanInfo = findInformant(beanClass);
  98. if (beanInfo == null) {
  99. beanInfo = new JXPathBasicBeanInfo(beanClass);
  100. }
  101. }
  102. byClass.put(beanClass, beanInfo);
  103. }
  104. return beanInfo;
  105. }
  106. /**
  107. * Find a dynamic bean info if available for any superclasses or
  108. * interfaces.
  109. */
  110. private static JXPathBeanInfo findDynamicBeanInfo(Class beanClass) {
  111. JXPathBeanInfo beanInfo = null;
  112. if (beanClass.isInterface()) {
  113. beanInfo = (JXPathBeanInfo) byInterface.get(beanClass);
  114. if (beanInfo != null && beanInfo.isDynamic()) {
  115. return beanInfo;
  116. }
  117. }
  118. Class interfaces[] = beanClass.getInterfaces();
  119. if (interfaces != null) {
  120. for (int i = 0; i < interfaces.length; i++) {
  121. beanInfo = findDynamicBeanInfo(interfaces[i]);
  122. if (beanInfo != null && beanInfo.isDynamic()) {
  123. return beanInfo;
  124. }
  125. }
  126. }
  127. Class sup = beanClass.getSuperclass();
  128. if (sup != null) {
  129. beanInfo = (JXPathBeanInfo) byClass.get(sup);
  130. if (beanInfo != null && beanInfo.isDynamic()) {
  131. return beanInfo;
  132. }
  133. return findDynamicBeanInfo(sup);
  134. }
  135. return null;
  136. }
  137. private static synchronized JXPathBeanInfo findInformant(Class beanClass) {
  138. String name = beanClass.getName() + "XBeanInfo";
  139. try {
  140. return (JXPathBeanInfo) instantiate(beanClass, name);
  141. }
  142. catch (Exception ex) {
  143. // Just drop through
  144. }
  145. // Now try checking if the bean is its own JXPathBeanInfo.
  146. try {
  147. if (JXPathBeanInfo.class.isAssignableFrom(beanClass)) {
  148. return (JXPathBeanInfo) beanClass.newInstance();
  149. }
  150. }
  151. catch (Exception ex) {
  152. // Just drop through
  153. }
  154. return null;
  155. }
  156. /**
  157. * Try to create an instance of a named class.
  158. * First try the classloader of "sibling", then try the system
  159. * classloader.
  160. */
  161. private static Object instantiate(Class sibling, String className)
  162. throws Exception
  163. {
  164. // First check with sibling's classloader (if any).
  165. ClassLoader cl = sibling.getClassLoader();
  166. if (cl != null) {
  167. try {
  168. Class cls = cl.loadClass(className);
  169. return cls.newInstance();
  170. }
  171. catch (Exception ex) {
  172. // Just drop through and try the system classloader.
  173. }
  174. }
  175. // Now try the bootstrap classloader.
  176. Class cls = Class.forName(className);
  177. return cls.newInstance();
  178. }
  179. }