    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 }