Coverage Report - org.apache.commons.clazz.reflect.ReflectedClazz
 
Classes in this File Line Coverage Branch Coverage Complexity
ReflectedClazz
0%
0/176
0%
0/92
3
 
 1  
 /*
 2  
  * Copyright 2002-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.clazz.reflect;
 17  
 
 18  
 import java.beans.BeanInfo;
 19  
 import java.beans.IndexedPropertyDescriptor;
 20  
 import java.beans.Introspector;
 21  
 import java.beans.PropertyDescriptor;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Collections;
 24  
 import java.util.HashMap;
 25  
 import java.util.HashSet;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 
 29  
 import org.apache.commons.clazz.Clazz;
 30  
 import org.apache.commons.clazz.ClazzInstanceFactory;
 31  
 import org.apache.commons.clazz.ClazzLoader;
 32  
 import org.apache.commons.clazz.ClazzOperation;
 33  
 import org.apache.commons.clazz.ClazzProperty;
 34  
 import org.apache.commons.clazz.reflect.common.ReflectedListProperty;
 35  
 import org.apache.commons.clazz.reflect.common.ReflectedScalarProperty;
 36  
 
 37  
 /**
 38  
  * This implementation of Clazz is based on Java reflection. 
 39  
  * 
 40  
  * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
 41  
  * @version $Id: ReflectedClazz.java 155436 2005-02-26 13:17:48Z dirkv $
 42  
  */
 43  
 public abstract class ReflectedClazz extends Clazz {
 44  
 
 45  
     private Class instanceClass;
 46  
     private boolean superClazzKnown;
 47  
     private Clazz superClazz;
 48  
 
 49  
     /**
 50  
      * This map includes all properties, declared and inherited
 51  
      */
 52  
     private Map propertyMap;
 53  
     private List propertyList;
 54  
     private List declaredPropertyList;
 55  
 
 56  
     /**
 57  
      * This map includes all operations, declared and inherited
 58  
      */
 59  
     private Map operationMap;
 60  
     private List operationList;
 61  
     private List declaredOperationList;
 62  
 
 63  
     /**
 64  
      * This map includes all factories
 65  
      */
 66  
     private Map factoryMap;
 67  
     private List factoryList;
 68  
     
 69  0
     private boolean hasLookedForBeanInfo = false;
 70  
     private BeanInfo beanInfo;
 71  
 
 72  
     /**
 73  
      * 
 74  
      * @param loader
 75  
      * @param instanceClass
 76  
      */
 77  
     public ReflectedClazz(ClazzLoader loader, Class instanceClass) {
 78  0
         super(loader, getCanonicalClassName(instanceClass));
 79  0
         this.instanceClass = instanceClass;
 80  0
     }
 81  
 
 82  
     public Class getInstanceClass() {
 83  0
         return instanceClass;
 84  
     }
 85  
 
 86  
     /**
 87  
      * The order of introspectors is significant, they are invoked sequencially.
 88  
      * Once a property has been recoginzed by an introspector, it will not be
 89  
      * overridden by subsequently invoked ones.
 90  
      */
 91  
     protected abstract ReflectedPropertyIntrospector[] 
 92  
             getPropertyIntrospectors();
 93  
 
 94  
     /**
 95  
      * @see #getPropertyIntrospectors()
 96  
      */
 97  
     protected abstract ReflectedOperationIntrospector[] 
 98  
             getOperationIntrospectors();
 99  
 
 100  
     /**
 101  
      * @see #getPropertyIntrospectors()
 102  
      */
 103  
     protected abstract ReflectedInstanceFactoryIntrospector[] 
 104  
             getInstanceFactoryIntrospectors();
 105  
 
 106  
     /**
 107  
      * Returns true if diagnostic is enabled for this clazz
 108  
      */
 109  
     public boolean isLoggingEnabled() {
 110  0
         return getClazzLoader().isLoggingEnabled(getName());
 111  
     }
 112  
 
 113  
     /**
 114  
      * Returns the class this ReflectedClazz is based upon.
 115  
      * @return Class
 116  
      */
 117  
     public Class getReflectedClass() {
 118  0
         return instanceClass;
 119  
     }
 120  
 
 121  
     /**
 122  
      * @see Clazz#getSuperclazz()
 123  
      */
 124  
     public Clazz getSuperclazz() {
 125  0
         if (!superClazzKnown) {
 126  0
             superClazzKnown = true;
 127  0
             Class superclass = instanceClass.getSuperclass();
 128  0
             if (superclass == null) {
 129  0
                 superClazz = null;
 130  
             }
 131  
             else {
 132  0
                 superClazz =
 133  
                     getClazzLoader().getClazzForName(superclass.getName());
 134  
             }
 135  
         }
 136  0
         return superClazz;
 137  
     }
 138  
 
 139  
     /**
 140  
      * Returns properties introduced by this clazz as compared 
 141  
      * to the superclazz.  Note, that some of the methods comprizing
 142  
      * a property may be declared on the superclass. This makes it
 143  
      * impossible to build a list of declared properties by looking
 144  
      * exclusively at the current class - we have to reflected all
 145  
      * properties of this clazz, then all properties of the superclazz
 146  
      * and then subtract the latter from the former.
 147  
      */
 148  
     public List getDeclaredProperties() {
 149  0
         if (declaredPropertyList == null) {
 150  0
             if (instanceClass.getSuperclass() == null) {
 151  0
                 declaredPropertyList = getProperties();
 152  
             }
 153  
             else {
 154  0
                 List superProperties = getSuperclazz().getProperties();
 155  0
                 if (superProperties.size() == 0) {
 156  0
                     declaredPropertyList = getProperties();
 157  
                 }
 158  
                 else {
 159  0
                     HashSet superNames = new HashSet();
 160  0
                     for (int i = 0; i < superProperties.size(); i++) {
 161  0
                         ClazzProperty property =
 162  
                             (ClazzProperty) superProperties.get(i);
 163  0
                         superNames.add(property.getName());
 164  
                     }
 165  
 
 166  0
                     List properties = getProperties();
 167  0
                     declaredPropertyList = new ArrayList();
 168  0
                     for (int i = 0; i < properties.size(); i++) {
 169  0
                         ClazzProperty property =
 170  
                             (ClazzProperty) properties.get(i);
 171  0
                         String name = property.getName();
 172  0
                         if (!superNames.contains(name)) {
 173  0
                             declaredPropertyList.add(property);
 174  
                         }
 175  
                     }
 176  
                 }
 177  
             }
 178  
         }
 179  0
         return declaredPropertyList;
 180  
     }
 181  
 
 182  
     /**
 183  
      */
 184  
     public List getProperties() {
 185  0
         if (propertyList == null) {
 186  0
             introspectProperties();
 187  0
             if (isLoggingEnabled()) {
 188  0
                 logPropertyParseResults();
 189  
             }
 190  
         }
 191  0
         return propertyList;
 192  
     }
 193  
 
 194  
     /**
 195  
      */
 196  
     public ClazzProperty getProperty(String name) {
 197  0
         if (propertyMap == null) {
 198  0
             introspectProperties();
 199  0
             if (isLoggingEnabled()) {
 200  0
                 logPropertyParseResults();
 201  
             }
 202  
         }
 203  0
         return (ClazzProperty) propertyMap.get(name);
 204  
     }
 205  
 
 206  
     protected void addProperty(ClazzProperty property) {
 207  0
         propertyMap.put(property.getName(), property);
 208  0
         if (property instanceof ReflectedProperty) {
 209  0
             String[] aliases = ((ReflectedProperty) property).getAliases();
 210  0
             for (int i = 0; i < aliases.length; i++) {
 211  0
                 propertyMap.put(aliases[i], property);
 212  
             }
 213  
         }
 214  0
         propertyList.add(property);
 215  0
     }
 216  
 
 217  
     /**
 218  
      */
 219  
     public List getOperations() {
 220  0
         if (operationList == null) {
 221  0
             introspectOperations();
 222  
 //          if (isLoggingEnabled()) {
 223  
 //              logOperationParseResults();
 224  
 //          }
 225  
         }
 226  0
         return operationList;
 227  
     }
 228  
 
 229  
     /**
 230  
      * @see Clazz#getDeclaredOperations()
 231  
      */
 232  
     public List getDeclaredOperations() {
 233  0
         if (declaredOperationList == null) {
 234  0
             introspectOperations();
 235  
 //          if (isLoggingEnabled()) {
 236  
 //              logOperationParseResults();
 237  
 //          }
 238  
         }
 239  0
         return declaredOperationList;
 240  
     }
 241  
 
 242  
     /**
 243  
      * @see Clazz#getOperation(java.lang.String)
 244  
      */
 245  
     public ClazzOperation getOperation(String signature) {
 246  0
         if (operationMap == null) {
 247  0
             introspectOperations();
 248  
 //          if (isLoggingEnabled()) {
 249  
 //              logOperationParseResults();
 250  
 //          }
 251  
         }
 252  0
         return (ClazzOperation) operationMap.get(signature);
 253  
     }
 254  
 
 255  
     protected void addOperation(ClazzOperation operation) {
 256  0
         operationMap.put(operation.getSignature(), operation);
 257  0
         declaredOperationList.add(operation);
 258  0
     }
 259  
 
 260  
     /**
 261  
      * Returns all InstanceFactories for this clazz.
 262  
      */
 263  
     public List getInstanceFactories() {
 264  0
         if (factoryList == null) {
 265  0
             introspectInstanceFactories();
 266  
 //          if (isLoggingEnabled()) {
 267  
 //              logInstanceFactoryParseResults();
 268  
 //          }
 269  
         }
 270  0
         return factoryList;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Returns the InstanceFactories that match the Predicate.
 275  
      */
 276  
     //    public List getInstanceFactories(Predicate predicate);
 277  
 
 278  
     /**
 279  
      * @see org.apache.commons.clazz.Clazz#getInstanceFactory(java.lang.String)
 280  
      */
 281  
     public ClazzInstanceFactory getInstanceFactory(String signature) {
 282  0
         if (factoryMap == null) {
 283  0
             introspectInstanceFactories();
 284  
 //          if (isLoggingEnabled()) {
 285  
 //              logInstanceFactoryParseResults();
 286  
 //          }
 287  
         }
 288  0
         if (signature == null) {
 289  0
             signature = "()";
 290  
         }
 291  0
         return (ClazzInstanceFactory) factoryMap.get(signature);
 292  
     }
 293  
 
 294  
     protected void addInstanceFactory(ClazzInstanceFactory factory) {
 295  0
         factoryMap.put(factory.getSignature(), factory);
 296  0
         factoryList.add(factory);
 297  0
     }
 298  
 
 299  
     /**
 300  
      * Overrides the default implementation, checks if the supplied clazz is
 301  
      * also a ReflectedClazz and if so invokes isAssignableFrom on the
 302  
      * corresponding java classes.
 303  
      */
 304  
     public boolean isAssignableFrom(Clazz clazz) {
 305  0
         if (clazz == this) {
 306  0
             return true;
 307  
         }
 308  0
         if (clazz instanceof ReflectedClazz) {
 309  0
             return getReflectedClass().isAssignableFrom(
 310  
                 ((ReflectedClazz) clazz).getReflectedClass());
 311  
         }
 312  0
         return super.isAssignableFrom(clazz);
 313  
     }
 314  
 
 315  
     /**
 316  
      * Override this method to provide an alternate way of mapping
 317  
      * fields and methods to properties.
 318  
      */
 319  
     protected void introspectProperties() {
 320  0
         propertyList = new ArrayList();
 321  0
         propertyMap = new HashMap();
 322  
 
 323  0
         BeanInfo beanInfo = getBeanInfo();
 324  
         
 325  0
         if (beanInfo != null) {
 326  0
             PropertyDescriptor pds[] = beanInfo.getPropertyDescriptors();
 327  0
             if (pds != null) {
 328  0
                 for (int i = 0; i < pds.length; i++) {
 329  0
                     PropertyDescriptor pd = pds[i];
 330  0
                     if (pd instanceof IndexedPropertyDescriptor) {
 331  0
                         IndexedPropertyDescriptor ipd =
 332  
                             (IndexedPropertyDescriptor) pd;
 333  0
                         ReflectedListProperty prop =
 334  
                             new ReflectedListProperty(this, ipd.getName());
 335  0
                         prop.setType(ipd.getPropertyType());
 336  0
                         prop.setContentType(ipd.getIndexedPropertyType());
 337  0
                         prop.setReadMethod(ipd.getReadMethod());
 338  0
                         prop.setWriteMethod(ipd.getWriteMethod());
 339  0
                         prop.setGetMethod(ipd.getIndexedReadMethod());
 340  0
                         prop.setSetMethod(ipd.getIndexedWriteMethod());
 341  0
                         addProperty(prop);
 342  0
                     }
 343  
                     else {
 344  0
                         ReflectedScalarProperty prop =
 345  
                             new ReflectedScalarProperty(this, pd.getName());
 346  0
                         prop.setType(pd.getPropertyType());
 347  0
                         prop.setReadMethod(pd.getReadMethod());
 348  0
                         prop.setWriteMethod(pd.getWriteMethod());
 349  0
                         addProperty(prop);
 350  
                     }
 351  
                 }
 352  
             }
 353  0
         }
 354  
         else {
 355  0
             ReflectedPropertyIntrospector introspectors[] =
 356  
                 getPropertyIntrospectors();
 357  
     
 358  0
             if (introspectors != null) {
 359  0
                 for (int i = 0; i < introspectors.length; i++) {
 360  0
                     List properties =
 361  
                         introspectors[i].introspectProperties(
 362  
                             this,
 363  
                             instanceClass);
 364  0
                     for (int j = 0; j < properties.size(); j++) {
 365  0
                         ClazzProperty property =
 366  
                             (ClazzProperty) properties.get(j);
 367  0
                         addProperty(property);
 368  
                     }
 369  
                 }
 370  
             }
 371  
         }
 372  0
     }
 373  
     
 374  
     /**
 375  
      * Performs BeanInfo lookup in the same manner as the standard
 376  
      * java.beans.Introspector.
 377  
      */
 378  
     protected BeanInfo getBeanInfo() {
 379  0
         if (!hasLookedForBeanInfo) {
 380  0
             hasLookedForBeanInfo = true;            
 381  0
             beanInfo = lookupBeanInfo();
 382  
         }
 383  0
         return beanInfo;
 384  
     }
 385  
 
 386  
     /**
 387  
      * Search for a custom implementation of BeanInfo according to
 388  
      * the JavaBeans standard definition.
 389  
      */
 390  
     private BeanInfo lookupBeanInfo() {
 391  0
         ClassLoader classLoader = instanceClass.getClassLoader();
 392  
         
 393  0
         String name = instanceClass.getName() + "BeanInfo";
 394  
         try {
 395  0
             return (BeanInfo) instantiate(classLoader, name);
 396  
         }
 397  0
         catch (Exception ex) {
 398  
             // Just drop through
 399  
         }
 400  
         
 401  
         // Now try checking if the bean is its own BeanInfo.
 402  
         try {
 403  0
             if (BeanInfo.class.isAssignableFrom(instanceClass)) {
 404  0
                 return (BeanInfo) instanceClass.newInstance();
 405  
             }
 406  
         }
 407  0
         catch (Exception ex) {
 408  
             // Just drop through
 409  0
         }
 410  
         
 411  
         // Now try looking for <searchPath>.fooBeanInfo
 412  0
         while (name.indexOf('.') > 0) {
 413  0
             name = name.substring(name.indexOf('.') + 1);
 414  
         }
 415  
         
 416  0
         String[] searchPath = Introspector.getBeanInfoSearchPath();
 417  0
         for (int i = 0; i < searchPath.length; i++) {
 418  
             try {
 419  0
                 String fullName = searchPath[i] + "." + name;
 420  0
                 return (BeanInfo) instantiate(classLoader, fullName);
 421  
             }
 422  0
             catch (Exception ex) {
 423  
                 // Silently ignore any errors.
 424  
             }
 425  
         }
 426  0
         return null;
 427  
     }
 428  
     
 429  
     static Object instantiate(ClassLoader classLoader, String className)
 430  
         throws
 431  
             InstantiationException,
 432  
             IllegalAccessException,
 433  
             ClassNotFoundException 
 434  
     {
 435  0
         if (classLoader != null) {
 436  
             try {
 437  0
                 Class cls = classLoader.loadClass(className);
 438  0
                 return cls.newInstance();
 439  
             }
 440  0
             catch (Exception ex) {
 441  
                 // Just drop through and try the system classloader.
 442  
             }
 443  
         }
 444  
         
 445  
         // Now try the default classloader.
 446  0
         Class cls = Class.forName(className);
 447  0
         return cls.newInstance();
 448  
     }
 449  
 
 450  
     /**
 451  
      * Override this method to provide an alternate way of mapping
 452  
      * methods (and possibly fields) to Operations.
 453  
      */
 454  
     protected void introspectOperations() {
 455  0
         declaredOperationList = new ArrayList();
 456  0
         operationMap = new HashMap();
 457  0
         operationList = new ArrayList();
 458  
 
 459  0
         Clazz superClazz = getSuperclazz();
 460  0
         if (superClazz != null) {
 461  0
             List superOps = superClazz.getOperations();
 462  0
             for (int i = 0; i < superOps.size(); i++) {
 463  0
                 ClazzOperation operation = (ClazzOperation) superOps.get(i);
 464  0
                 operationMap.put(operation.getSignature(), operation);
 465  
             }
 466  
         }
 467  
 
 468  0
         ReflectedOperationIntrospector introspectors[] =
 469  
             getOperationIntrospectors();
 470  
 
 471  0
         if (introspectors != null) {
 472  0
             for (int i = 0; i < introspectors.length; i++) {
 473  0
                 List operations =
 474  
                     introspectors[i].introspectOperations(this, instanceClass);
 475  0
                 for (int j = 0; j < operations.size(); j++) {
 476  0
                     ClazzOperation operation =
 477  
                         (ClazzOperation) operations.get(j);
 478  0
                     addOperation(operation);
 479  
                 }
 480  
             }
 481  
         }
 482  0
         operationList.addAll(operationMap.values());
 483  0
     }
 484  
 
 485  
     /**
 486  
      * Override this method to provide an alternate way of mapping
 487  
      * constructors, methods (and possibly fields) to InstanceFactories.
 488  
      */
 489  
     protected void introspectInstanceFactories() {
 490  0
         factoryMap = new HashMap();
 491  0
         factoryList = new ArrayList();
 492  
 
 493  0
         ReflectedInstanceFactoryIntrospector introspectors[] =
 494  
             getInstanceFactoryIntrospectors();
 495  
 
 496  0
         if (introspectors != null) {
 497  0
             for (int i = 0; i < introspectors.length; i++) {
 498  0
                 List factories =
 499  
                     introspectors[i].introspectInstanceFactories(
 500  
                         this,
 501  
                         instanceClass);
 502  0
                 for (int j = 0; j < factories.size(); j++) {
 503  0
                     ClazzInstanceFactory factory =
 504  
                         (ClazzInstanceFactory) factories.get(j);
 505  0
                     addInstanceFactory(factory);
 506  
                 }
 507  
             }
 508  
         }
 509  0
     }
 510  
 
 511  
     private List propertyParseResults;
 512  
 
 513  
     /**
 514  
      * Called by ReflectedPropertyIntrospector's to log results of
 515  
      * introspection, successful or not.
 516  
      */
 517  
     public void logPropertyParseResults(Object parseResults) {
 518  0
         if (propertyParseResults == null) {
 519  0
             propertyParseResults = new ArrayList();
 520  
         }
 521  0
         propertyParseResults.add(parseResults);
 522  0
     }
 523  
 
 524  
     /**
 525  
      * Prints diagnostics of property introspection.
 526  
      */
 527  
     protected void logPropertyParseResults() {
 528  0
         if (propertyParseResults == null) {
 529  0
             return;
 530  
         }
 531  
 
 532  
         // PropertyParseResults are supposed to implement Comparable
 533  0
         Collections.sort(propertyParseResults);
 534  
 
 535  
         // @todo: use a logger
 536  0
         System.err.println("[Clazz: " + getName());
 537  0
         for (int i = 0; i < propertyParseResults.size(); i++) {
 538  0
             System.err.println(propertyParseResults.get(i));
 539  
         }
 540  0
         System.err.println("]");
 541  0
     }
 542  
 }