Coverage Report - org.apache.commons.el.BeanInfoManager
 
Classes in this File Line Coverage Branch Coverage Complexity
BeanInfoManager
0%
0/87
0%
0/40
3.071
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.el;
 18  
 
 19  
 import java.beans.BeanInfo;
 20  
 import java.beans.EventSetDescriptor;
 21  
 import java.beans.IndexedPropertyDescriptor;
 22  
 import java.beans.IntrospectionException;
 23  
 import java.beans.Introspector;
 24  
 import java.beans.PropertyDescriptor;
 25  
 import java.lang.reflect.Method;
 26  
 import java.lang.reflect.Modifier;
 27  
 import java.util.HashMap;
 28  
 import java.util.Map;
 29  
 
 30  
 import javax.servlet.jsp.el.ELException;
 31  
 
 32  
 import org.apache.commons.logging.Log;
 33  
 import org.apache.commons.logging.LogFactory;
 34  
 
 35  
 /**
 36  
  *
 37  
  * <p>Manages the BeanInfo for one class - contains the BeanInfo, and
 38  
  * also a mapping from property name to BeanInfoProperty.  There are
 39  
  * also static methods for accessing the BeanInfoManager for a class -
 40  
  * those mappings are cached permanently so that once the
 41  
  * BeanInfoManager is calculated, it doesn't have to be calculated
 42  
  * again.
 43  
  * 
 44  
  * @author Nathan Abramson - Art Technology Group
 45  
  * @version $Change: 181181 $$DateTime: 2001/06/26 09:55:09 $$Author: bayard $
 46  
  **/
 47  
 
 48  
 public class BeanInfoManager
 49  
 {
 50  
     //-------------------------------------
 51  
     // Constants
 52  
     //-------------------------------------
 53  0
     private static Log log = LogFactory.getLog(BeanInfoManager.class);
 54  
     
 55  
   //-------------------------------------
 56  
   // Properties
 57  
   //-------------------------------------
 58  
   // property beanClass
 59  
 
 60  
   Class mBeanClass;
 61  
   public Class getBeanClass ()
 62  0
   { return mBeanClass; }
 63  
 
 64  
   //-------------------------------------
 65  
   // Member variables
 66  
   //-------------------------------------
 67  
 
 68  
   // The BeanInfo
 69  
   BeanInfo mBeanInfo;
 70  
 
 71  
   // Mapping from property name to BeanInfoProperty
 72  
   Map mPropertyByName;
 73  
 
 74  
   // Mapping from property name to BeanInfoIndexedProperty
 75  
   Map mIndexedPropertyByName;
 76  
 
 77  
   // Mapping from event set name to event set descriptor
 78  
   Map mEventSetByName;
 79  
 
 80  
   // Flag if this is initialized
 81  
   boolean mInitialized;
 82  
 
 83  
   // The global mapping from class to BeanInfoManager
 84  0
   static Map mBeanInfoManagerByClass = new HashMap ();
 85  
 
 86  
   //-------------------------------------
 87  
   /**
 88  
    *
 89  
    * Constructor
 90  
    **/
 91  
   BeanInfoManager (Class pBeanClass)
 92  0
   {
 93  0
     mBeanClass = pBeanClass;
 94  0
   }
 95  
 
 96  
   //-------------------------------------
 97  
   /**
 98  
    *
 99  
    * Returns the BeanInfoManager for the specified class
 100  
    **/
 101  
   public static BeanInfoManager getBeanInfoManager (Class pClass)
 102  
   {
 103  0
     BeanInfoManager ret = (BeanInfoManager) 
 104  
       mBeanInfoManagerByClass.get (pClass);
 105  0
     if (ret == null) {
 106  0
       ret = createBeanInfoManager (pClass);
 107  
     }
 108  0
     return ret;
 109  
   }
 110  
 
 111  
   //-------------------------------------
 112  
   /**
 113  
    *
 114  
    * Creates and registers the BeanInfoManager for the given class if
 115  
    * it isn't already registered.
 116  
    **/
 117  
   static synchronized BeanInfoManager createBeanInfoManager (Class pClass)
 118  
   {
 119  
     // Because this method is synchronized statically, the
 120  
     // BeanInfoManager is not initialized at this time (otherwise it
 121  
     // could end up being a bottleneck for the entire system).  It is
 122  
     // put into the map in an uninitialized state.  The first time
 123  
     // someone tries to use it, it will be initialized (with proper
 124  
     // synchronizations in place to make sure it is only initialized
 125  
     // once).
 126  
 
 127  0
     BeanInfoManager ret = (BeanInfoManager) 
 128  
       mBeanInfoManagerByClass.get (pClass);
 129  0
     if (ret == null) {
 130  0
       ret = new BeanInfoManager (pClass);
 131  0
       mBeanInfoManagerByClass.put (pClass, ret);
 132  
     }
 133  0
     return ret;
 134  
   }
 135  
 
 136  
   //-------------------------------------
 137  
   /**
 138  
    *
 139  
    * Returns the BeanInfoProperty for the specified property in the
 140  
    * given class, or null if not found.
 141  
    **/
 142  
   public static BeanInfoProperty getBeanInfoProperty
 143  
     (Class pClass,
 144  
      String pPropertyName)
 145  
     throws ELException
 146  
   {
 147  0
     return getBeanInfoManager (pClass).getProperty (pPropertyName);
 148  
   }
 149  
 
 150  
   //-------------------------------------
 151  
   /**
 152  
    *
 153  
    * Returns the BeanInfoIndexedProperty for the specified property in
 154  
    * the given class, or null if not found.
 155  
    **/
 156  
   public static BeanInfoIndexedProperty getBeanInfoIndexedProperty
 157  
     (Class pClass,
 158  
      String pIndexedPropertyName)
 159  
     throws ELException
 160  
   {
 161  0
     return getBeanInfoManager 
 162  
       (pClass).getIndexedProperty (pIndexedPropertyName);
 163  
   }
 164  
 
 165  
   //-------------------------------------
 166  
   /**
 167  
    *
 168  
    * Makes sure that this class has been initialized, and synchronizes
 169  
    * the initialization if it's required.
 170  
    **/
 171  
   void checkInitialized ()
 172  
     throws ELException
 173  
   {
 174  0
     if (!mInitialized) {
 175  0
       synchronized (this) {
 176  0
         if (!mInitialized) {
 177  0
           initialize();
 178  0
           mInitialized = true;
 179  
         }
 180  0
       }
 181  
     }
 182  0
   }
 183  
 
 184  
   //-------------------------------------
 185  
   /**
 186  
    *
 187  
    * Initializes by mapping property names to BeanInfoProperties
 188  
    **/
 189  
   void initialize ()
 190  
     throws ELException
 191  
   {
 192  
     try {
 193  0
       mBeanInfo = Introspector.getBeanInfo (mBeanClass);
 194  
 
 195  0
       mPropertyByName = new HashMap ();
 196  0
       mIndexedPropertyByName = new HashMap ();
 197  0
       PropertyDescriptor [] pds = mBeanInfo.getPropertyDescriptors ();
 198  0
       for (int i = 0; pds != null && i < pds.length; i++) {
 199  
         // Treat as both an indexed property and a normal property
 200  0
         PropertyDescriptor pd = pds [i];
 201  0
         if (pd instanceof IndexedPropertyDescriptor) {
 202  0
           IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
 203  0
           Method readMethod = getPublicMethod (ipd.getIndexedReadMethod ());
 204  0
           Method writeMethod = getPublicMethod (ipd.getIndexedWriteMethod ());
 205  0
           BeanInfoIndexedProperty property = new BeanInfoIndexedProperty
 206  
             (readMethod,
 207  
              writeMethod,
 208  
              ipd);
 209  
 
 210  0
           mIndexedPropertyByName.put (ipd.getName (), property);
 211  
         }
 212  
 
 213  0
         Method readMethod = getPublicMethod (pd.getReadMethod ());
 214  0
         Method writeMethod = getPublicMethod (pd.getWriteMethod ());
 215  0
         BeanInfoProperty property = new BeanInfoProperty
 216  
           (readMethod,
 217  
            writeMethod,
 218  
            pd);
 219  
 
 220  0
         mPropertyByName.put (pd.getName (), property);
 221  
       }
 222  
 
 223  0
       mEventSetByName = new HashMap ();
 224  0
       EventSetDescriptor [] esds = mBeanInfo.getEventSetDescriptors ();
 225  0
       for (int i = 0; esds != null && i < esds.length; i++) {
 226  0
         EventSetDescriptor esd = esds [i];
 227  0
         mEventSetByName.put (esd.getName (), esd);
 228  
       }
 229  
     }
 230  0
     catch (IntrospectionException exc) {
 231  0
         if (log.isWarnEnabled()) {
 232  0
             log.warn(
 233  
                 MessageUtil.getMessageWithArgs(
 234  
                     Constants.EXCEPTION_GETTING_BEANINFO, mBeanClass.getName()), exc);
 235  
         }     
 236  0
     }
 237  0
   }
 238  
 
 239  
   //-------------------------------------
 240  
   /**
 241  
    *
 242  
    * Returns the BeanInfo for the class
 243  
    **/
 244  
   BeanInfo getBeanInfo ()
 245  
     throws ELException
 246  
   {
 247  0
     checkInitialized();
 248  0
     return mBeanInfo;
 249  
   }
 250  
 
 251  
   //-------------------------------------
 252  
   /**
 253  
    *
 254  
    * Returns the BeanInfoProperty for the given property name, or null
 255  
    * if not found.
 256  
    **/
 257  
   public BeanInfoProperty getProperty (String pPropertyName)
 258  
     throws ELException
 259  
   {
 260  0
     checkInitialized();
 261  0
     return (BeanInfoProperty) mPropertyByName.get (pPropertyName);
 262  
   }
 263  
 
 264  
   //-------------------------------------
 265  
   /**
 266  
    *
 267  
    * Returns the BeanInfoIndexedProperty for the given property name,
 268  
    * or null if not found.
 269  
    **/
 270  
   public BeanInfoIndexedProperty getIndexedProperty 
 271  
     (String pIndexedPropertyName)
 272  
     throws ELException
 273  
   {
 274  0
     checkInitialized();
 275  0
     return (BeanInfoIndexedProperty) 
 276  
       mIndexedPropertyByName.get (pIndexedPropertyName);
 277  
   }
 278  
 
 279  
   //-------------------------------------
 280  
   /**
 281  
    *
 282  
    * Returns the EventSetDescriptor for the given event set name, or
 283  
    * null if not found.
 284  
    **/
 285  
   public EventSetDescriptor getEventSet (String pEventSetName)
 286  
     throws ELException
 287  
   {
 288  0
     checkInitialized();
 289  0
     return (EventSetDescriptor) mEventSetByName.get (pEventSetName);
 290  
   }
 291  
 
 292  
   //-------------------------------------
 293  
   // Finding the public version of a method - if a PropertyDescriptor
 294  
   // is obtained for a non-public class that implements a public
 295  
   // interface, the read/write methods will be for the class, and
 296  
   // therefore inaccessible.  To correct this, a version of the same
 297  
   // method must be found in a superclass or interface.
 298  
   //-------------------------------------
 299  
   /**
 300  
    *
 301  
    * Returns a publicly-accessible version of the given method, by
 302  
    * searching for a public declaring class.
 303  
    **/
 304  
   static Method getPublicMethod (Method pMethod)
 305  
   {
 306  0
     if (pMethod == null) {
 307  0
       return null;
 308  
     }
 309  
 
 310  
     // See if the method is already available from a public class
 311  0
     Class cl = pMethod.getDeclaringClass ();
 312  0
     if (Modifier.isPublic (cl.getModifiers ())) {
 313  0
       return pMethod;
 314  
     }
 315  
 
 316  
     // Otherwise, try to find a public class that declares the method
 317  0
     Method ret = getPublicMethod (cl, pMethod);
 318  0
     if (ret != null) {
 319  0
       return ret;
 320  
     }
 321  
     else {
 322  0
       return pMethod;
 323  
     }
 324  
   }
 325  
 
 326  
   //-------------------------------------
 327  
   /**
 328  
    *
 329  
    * If the given class is public and has a Method that declares the
 330  
    * same name and arguments as the given method, then that method is
 331  
    * returned.  Otherwise the superclass and interfaces are searched
 332  
    * recursively.
 333  
    **/
 334  
   static Method getPublicMethod (Class pClass,
 335  
                                  Method pMethod)
 336  
   {
 337  
     // See if this is a public class declaring the method
 338  0
     if (Modifier.isPublic (pClass.getModifiers ())) {
 339  
       try {
 340  
         Method m;
 341  
         try {
 342  0
           m = pClass.getDeclaredMethod (pMethod.getName (),
 343  
                                              pMethod.getParameterTypes ());
 344  0
         } catch (java.security.AccessControlException ex) {
 345  
           // kludge to accommodate J2EE RI's default settings
 346  
           // TODO: see if we can simply replace
 347  
           //       getDeclaredMethod() with getMethod() ...?
 348  0
           m = pClass.getMethod(pMethod.getName (),
 349  
                                              pMethod.getParameterTypes ());
 350  0
         }
 351  0
         if (Modifier.isPublic (m.getModifiers ())) {
 352  0
           return m;
 353  
         }
 354  
       }
 355  0
       catch (NoSuchMethodException exc) {}
 356  
     }
 357  
 
 358  
     // Search the interfaces
 359  
     {
 360  0
       Class [] interfaces = pClass.getInterfaces ();
 361  0
       if (interfaces != null) {
 362  0
         for (int i = 0; i < interfaces.length; i++) {
 363  0
           Method m = getPublicMethod (interfaces [i], pMethod);
 364  0
           if (m != null) {
 365  0
             return m;
 366  
           }
 367  
         }
 368  
       }
 369  
     }
 370  
 
 371  
     // Search the superclass
 372  
     {
 373  0
       Class superclass = pClass.getSuperclass ();
 374  0
       if (superclass != null) {
 375  0
         Method m = getPublicMethod (superclass, pMethod);
 376  0
         if (m != null) {
 377  0
           return m;
 378  
         }
 379  
       }
 380  
     }
 381  
 
 382  0
     return null;
 383  
   }
 384  
 
 385  
   //-------------------------------------
 386  
 }