View Javadoc

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