001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.jxpath; 018 019 import java.util.Date; 020 import java.util.Map; 021 import java.util.HashMap; 022 023 /** 024 * JXPathIntrospector maintains a registry of {@link JXPathBeanInfo 025 * JXPathBeanInfo} objects for Java classes. 026 * 027 * @author Dmitri Plotnikov 028 * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $ 029 */ 030 public class JXPathIntrospector { 031 032 private static HashMap byClass = new HashMap(); 033 private static HashMap byInterface = new HashMap(); 034 035 static { 036 registerAtomicClass(Class.class); 037 registerAtomicClass(Boolean.TYPE); 038 registerAtomicClass(Boolean.class); 039 registerAtomicClass(Byte.TYPE); 040 registerAtomicClass(Byte.class); 041 registerAtomicClass(Character.TYPE); 042 registerAtomicClass(Character.class); 043 registerAtomicClass(Short.TYPE); 044 registerAtomicClass(Short.class); 045 registerAtomicClass(Integer.TYPE); 046 registerAtomicClass(Integer.class); 047 registerAtomicClass(Long.TYPE); 048 registerAtomicClass(Long.class); 049 registerAtomicClass(Float.TYPE); 050 registerAtomicClass(Float.class); 051 registerAtomicClass(Double.TYPE); 052 registerAtomicClass(Double.class); 053 registerAtomicClass(String.class); 054 registerAtomicClass(Date.class); 055 registerAtomicClass(java.sql.Date.class); 056 registerAtomicClass(java.sql.Time.class); 057 registerAtomicClass(java.sql.Timestamp.class); 058 059 registerDynamicClass(Map.class, MapDynamicPropertyHandler.class); 060 } 061 062 /** 063 * Automatically creates and registers a JXPathBeanInfo object 064 * for the specified class. That object returns true to isAtomic(). 065 * @param beanClass to register 066 */ 067 public static void registerAtomicClass(Class beanClass) { 068 byClass.put(beanClass, new JXPathBasicBeanInfo(beanClass, true)); 069 } 070 071 /** 072 * Automatically creates and registers a {@link JXPathBeanInfo} object 073 * for the specified class. That object returns true to 074 * {@link JXPathBeanInfo#isDynamic()}. 075 * 076 * @param beanClass to register 077 * @param dynamicPropertyHandlerClass to handle beanClass 078 */ 079 public static void registerDynamicClass(Class beanClass, 080 Class dynamicPropertyHandlerClass) { 081 JXPathBasicBeanInfo bi = 082 new JXPathBasicBeanInfo(beanClass, dynamicPropertyHandlerClass); 083 if (beanClass.isInterface()) { 084 byInterface.put(beanClass, bi); 085 } 086 else { 087 byClass.put(beanClass, bi); 088 } 089 } 090 091 /** 092 * Creates and registers a JXPathBeanInfo object for the supplied class. If 093 * the class has already been registered, returns the registered 094 * JXPathBeanInfo object. 095 * <p> 096 * The process of creation of JXPathBeanInfo is as follows: 097 * <ul> 098 * <li>If class named <code><beanClass>XBeanInfo</code> exists, 099 * an instance of that class is allocated. 100 * <li>Otherwise, an instance of {@link JXPathBasicBeanInfo 101 * JXPathBasicBeanInfo} is allocated. 102 * </ul> 103 * @param beanClass whose info to get 104 * @return JXPathBeanInfo 105 */ 106 public static JXPathBeanInfo getBeanInfo(Class beanClass) { 107 JXPathBeanInfo beanInfo = (JXPathBeanInfo) byClass.get(beanClass); 108 if (beanInfo == null) { 109 beanInfo = findDynamicBeanInfo(beanClass); 110 if (beanInfo == null) { 111 beanInfo = findInformant(beanClass); 112 if (beanInfo == null) { 113 beanInfo = new JXPathBasicBeanInfo(beanClass); 114 } 115 } 116 byClass.put(beanClass, beanInfo); 117 } 118 return beanInfo; 119 } 120 121 /** 122 * Find a dynamic bean info if available for any superclasses or 123 * interfaces. 124 * @param beanClass to search for 125 * @return JXPathBeanInfo 126 */ 127 private static JXPathBeanInfo findDynamicBeanInfo(Class beanClass) { 128 JXPathBeanInfo beanInfo = null; 129 if (beanClass.isInterface()) { 130 beanInfo = (JXPathBeanInfo) byInterface.get(beanClass); 131 if (beanInfo != null && beanInfo.isDynamic()) { 132 return beanInfo; 133 } 134 } 135 136 Class[] interfaces = beanClass.getInterfaces(); 137 if (interfaces != null) { 138 for (int i = 0; i < interfaces.length; i++) { 139 beanInfo = findDynamicBeanInfo(interfaces[i]); 140 if (beanInfo != null && beanInfo.isDynamic()) { 141 return beanInfo; 142 } 143 } 144 } 145 146 Class sup = beanClass.getSuperclass(); 147 if (sup != null) { 148 beanInfo = (JXPathBeanInfo) byClass.get(sup); 149 if (beanInfo != null && beanInfo.isDynamic()) { 150 return beanInfo; 151 } 152 return findDynamicBeanInfo(sup); 153 } 154 return null; 155 } 156 157 /** 158 * find a JXPathBeanInfo instance for the specified class. 159 * Similar to javax.beans property handler discovery; search for a 160 * class with "XBeanInfo" appended to beanClass.name, then check 161 * whether beanClass implements JXPathBeanInfo for itself. 162 * Invokes the default constructor for any class it finds. 163 * @param beanClass for which to look for an info provider 164 * @return JXPathBeanInfo instance or null if none found 165 */ 166 private static synchronized JXPathBeanInfo findInformant(Class beanClass) { 167 String name = beanClass.getName() + "XBeanInfo"; 168 try { 169 return (JXPathBeanInfo) instantiate(beanClass, name); 170 } 171 catch (Exception ex) { //NOPMD 172 // Just drop through 173 } 174 175 // Now try checking if the bean is its own JXPathBeanInfo. 176 try { 177 if (JXPathBeanInfo.class.isAssignableFrom(beanClass)) { 178 return (JXPathBeanInfo) beanClass.newInstance(); 179 } 180 } 181 catch (Exception ex) { //NOPMD 182 // Just drop through 183 } 184 185 return null; 186 } 187 188 /** 189 * Try to create an instance of a named class. 190 * First try the classloader of "sibling", then try the system 191 * classloader. 192 * @param sibling Class 193 * @param className to instantiate 194 * @return new Object 195 * @throws Exception if instantiation fails 196 */ 197 private static Object instantiate(Class sibling, String className) 198 throws Exception { 199 200 // First check with sibling's classloader (if any). 201 ClassLoader cl = sibling.getClassLoader(); 202 if (cl != null) { 203 try { 204 Class cls = cl.loadClass(className); 205 return cls.newInstance(); 206 } 207 catch (Exception ex) { //NOPMD 208 // Just drop through and try the system classloader. 209 } 210 } 211 212 // Now try the bootstrap classloader. 213 Class cls = Class.forName(className); 214 return cls.newInstance(); 215 } 216 }