001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath;
017:
018: import java.util.Date;
019: import java.util.Map;
020: import java.util.HashMap;
021:
022: /**
023: * JXPathIntrospector maintains a registry of {@link JXPathBeanInfo
024: * JXPathBeanInfo} objects for Java classes.
025: *
026: * @author Dmitri Plotnikov
027: * @version $Revision: 1.10 $ $Date: 2004/05/08 15:10:05 $
028: */
029: public class JXPathIntrospector {
030:
031: private static HashMap byClass = new HashMap();
032: private static HashMap byInterface = new HashMap();
033:
034: static {
035: registerAtomicClass(Class.class);
036: registerAtomicClass(Boolean.TYPE);
037: registerAtomicClass(Boolean.class);
038: registerAtomicClass(Byte.TYPE);
039: registerAtomicClass(Byte.class);
040: registerAtomicClass(Character.TYPE);
041: registerAtomicClass(Character.class);
042: registerAtomicClass(Short.TYPE);
043: registerAtomicClass(Short.class);
044: registerAtomicClass(Integer.TYPE);
045: registerAtomicClass(Integer.class);
046: registerAtomicClass(Long.TYPE);
047: registerAtomicClass(Long.class);
048: registerAtomicClass(Float.TYPE);
049: registerAtomicClass(Float.class);
050: registerAtomicClass(Double.TYPE);
051: registerAtomicClass(Double.class);
052: registerAtomicClass(String.class);
053: registerAtomicClass(Date.class);
054: registerAtomicClass(java.sql.Date.class);
055: registerAtomicClass(java.sql.Time.class);
056: registerAtomicClass(java.sql.Timestamp.class);
057:
058: registerDynamicClass(Map.class, MapDynamicPropertyHandler.class);
059: }
060:
061: /**
062: * Automatically creates and registers a JXPathBeanInfo object
063: * for the specified class. That object returns true to isAtomic().
064: */
065: public static void registerAtomicClass(Class beanClass) {
066: byClass
067: .put(beanClass,
068: new JXPathBasicBeanInfo(beanClass, true));
069: }
070:
071: /**
072: * Automatically creates and registers a JXPathBeanInfo object
073: * for the specified class. That object returns true to isDynamic().
074: */
075: public static void registerDynamicClass(Class beanClass,
076: Class dynamicPropertyHandlerClass) {
077: JXPathBasicBeanInfo bi = new JXPathBasicBeanInfo(beanClass,
078: dynamicPropertyHandlerClass);
079: if (beanClass.isInterface()) {
080: byInterface.put(beanClass, bi);
081: } else {
082: byClass.put(beanClass, bi);
083: }
084: }
085:
086: /**
087: * Creates and registers a JXPathBeanInfo object for the supplied class. If
088: * the class has already been registered, returns the registered
089: * JXPathBeanInfo object.
090: * <p>
091: * The process of creation of JXPathBeanInfo is as follows:
092: * <ul>
093: * <li>If class named <code><beanClass>XBeanInfo</code> exists,
094: * an instance of that class is allocated.
095: * <li>Otherwise, an instance of {@link JXPathBasicBeanInfo
096: * JXPathBasicBeanInfo} is allocated.
097: * </ul>
098: */
099: public static JXPathBeanInfo getBeanInfo(Class beanClass) {
100: JXPathBeanInfo beanInfo = (JXPathBeanInfo) byClass
101: .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(
150: Class beanClass) {
151: String name = beanClass.getName() + "XBeanInfo";
152: try {
153: return (JXPathBeanInfo) instantiate(beanClass, name);
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: } catch (Exception ex) {
164: // Just drop through
165: }
166:
167: return null;
168: }
169:
170: /**
171: * Try to create an instance of a named class.
172: * First try the classloader of "sibling", then try the system
173: * classloader.
174: */
175: private static Object instantiate(Class sibling, String className)
176: throws Exception {
177:
178: // First check with sibling's classloader (if any).
179: ClassLoader cl = sibling.getClassLoader();
180: if (cl != null) {
181: try {
182: Class cls = cl.loadClass(className);
183: return cls.newInstance();
184: } catch (Exception ex) {
185: // Just drop through and try the system classloader.
186: }
187: }
188:
189: // Now try the bootstrap classloader.
190: Class cls = Class.forName(className);
191: return cls.newInstance();
192: }
193: }
|