blob: edaafc679be4957c28253a022ba0ffa1e2a256e6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jxpath;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
/**
* JXPathIntrospector maintains a registry of {@link JXPathBeanInfo
* JXPathBeanInfo} objects for Java classes.
*
* @author Dmitri Plotnikov
* @version $Revision$ $Date$
*/
public class JXPathIntrospector {
private static HashMap byClass = new HashMap();
private static HashMap byInterface = new HashMap();
static {
registerAtomicClass(Class.class);
registerAtomicClass(Boolean.TYPE);
registerAtomicClass(Boolean.class);
registerAtomicClass(Byte.TYPE);
registerAtomicClass(Byte.class);
registerAtomicClass(Character.TYPE);
registerAtomicClass(Character.class);
registerAtomicClass(Short.TYPE);
registerAtomicClass(Short.class);
registerAtomicClass(Integer.TYPE);
registerAtomicClass(Integer.class);
registerAtomicClass(Long.TYPE);
registerAtomicClass(Long.class);
registerAtomicClass(Float.TYPE);
registerAtomicClass(Float.class);
registerAtomicClass(Double.TYPE);
registerAtomicClass(Double.class);
registerAtomicClass(String.class);
registerAtomicClass(Date.class);
registerAtomicClass(java.sql.Date.class);
registerAtomicClass(java.sql.Time.class);
registerAtomicClass(java.sql.Timestamp.class);
registerDynamicClass(Map.class, MapDynamicPropertyHandler.class);
}
/**
* Automatically creates and registers a JXPathBeanInfo object
* for the specified class. That object returns true to isAtomic().
* @param beanClass to register
*/
public static void registerAtomicClass(Class beanClass) {
byClass.put(beanClass, new JXPathBasicBeanInfo(beanClass, true));
}
/**
* Automatically creates and registers a {@link JXPathBeanInfo} object
* for the specified class. That object returns true to
* {@link JXPathBeanInfo#isDynamic()}.
*
* @param beanClass to register
* @param dynamicPropertyHandlerClass to handle beanClass
*/
public static void registerDynamicClass(Class beanClass,
Class dynamicPropertyHandlerClass) {
JXPathBasicBeanInfo bi =
new JXPathBasicBeanInfo(beanClass, dynamicPropertyHandlerClass);
if (beanClass.isInterface()) {
byInterface.put(beanClass, bi);
}
else {
byClass.put(beanClass, bi);
}
}
/**
* Creates and registers a JXPathBeanInfo object for the supplied class. If
* the class has already been registered, returns the registered
* JXPathBeanInfo object.
* <p>
* The process of creation of JXPathBeanInfo is as follows:
* <ul>
* <li>If class named <code>&lt;beanClass&gt;XBeanInfo</code> exists,
* an instance of that class is allocated.
* <li>Otherwise, an instance of {@link JXPathBasicBeanInfo
* JXPathBasicBeanInfo} is allocated.
* </ul>
* @param beanClass whose info to get
* @return JXPathBeanInfo
*/
public static JXPathBeanInfo getBeanInfo(Class beanClass) {
JXPathBeanInfo beanInfo = (JXPathBeanInfo) byClass.get(beanClass);
if (beanInfo == null) {
beanInfo = findDynamicBeanInfo(beanClass);
if (beanInfo == null) {
beanInfo = findInformant(beanClass);
if (beanInfo == null) {
beanInfo = new JXPathBasicBeanInfo(beanClass);
}
}
byClass.put(beanClass, beanInfo);
}
return beanInfo;
}
/**
* Find a dynamic bean info if available for any superclasses or
* interfaces.
* @param beanClass to search for
* @return JXPathBeanInfo
*/
private static JXPathBeanInfo findDynamicBeanInfo(Class beanClass) {
JXPathBeanInfo beanInfo = null;
if (beanClass.isInterface()) {
beanInfo = (JXPathBeanInfo) byInterface.get(beanClass);
if (beanInfo != null && beanInfo.isDynamic()) {
return beanInfo;
}
}
Class[] interfaces = beanClass.getInterfaces();
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
beanInfo = findDynamicBeanInfo(interfaces[i]);
if (beanInfo != null && beanInfo.isDynamic()) {
return beanInfo;
}
}
}
Class sup = beanClass.getSuperclass();
if (sup != null) {
beanInfo = (JXPathBeanInfo) byClass.get(sup);
if (beanInfo != null && beanInfo.isDynamic()) {
return beanInfo;
}
return findDynamicBeanInfo(sup);
}
return null;
}
/**
* find a JXPathBeanInfo instance for the specified class.
* Similar to javax.beans property handler discovery; search for a
* class with "XBeanInfo" appended to beanClass.name, then check
* whether beanClass implements JXPathBeanInfo for itself.
* Invokes the default constructor for any class it finds.
* @param beanClass for which to look for an info provider
* @return JXPathBeanInfo instance or null if none found
*/
private static synchronized JXPathBeanInfo findInformant(Class beanClass) {
String name = beanClass.getName() + "XBeanInfo";
try {
return (JXPathBeanInfo) instantiate(beanClass, name);
}
catch (Exception ex) {
// Just drop through
}
// Now try checking if the bean is its own JXPathBeanInfo.
try {
if (JXPathBeanInfo.class.isAssignableFrom(beanClass)) {
return (JXPathBeanInfo) beanClass.newInstance();
}
}
catch (Exception ex) {
// Just drop through
}
return null;
}
/**
* Try to create an instance of a named class.
* First try the classloader of "sibling", then try the system
* classloader.
* @param sibling Class
* @param className to instantiate
* @return new Object
* @throws Exception if instantiation fails
*/
private static Object instantiate(Class sibling, String className)
throws Exception {
// First check with sibling's classloader (if any).
ClassLoader cl = sibling.getClassLoader();
if (cl != null) {
try {
Class cls = cl.loadClass(className);
return cls.newInstance();
}
catch (Exception ex) {
// Just drop through and try the system classloader.
}
}
// Now try the bootstrap classloader.
Class cls = Class.forName(className);
return cls.newInstance();
}
}