Coverage Report - org.kuali.rice.kns.datadictionary.DataDictionary
 
Classes in this File Line Coverage Branch Coverage Complexity
DataDictionary
0%
0/224
0%
0/98
3.394
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 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  
 
 17  
 package org.kuali.rice.kns.datadictionary;
 18  
 
 19  
 import java.beans.IntrospectionException;
 20  
 import java.beans.PropertyDescriptor;
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Collection;
 25  
 import java.util.HashMap;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 import java.util.Set;
 29  
 import java.util.TreeMap;
 30  
 
 31  
 import org.apache.commons.lang.StringUtils;
 32  
 import org.apache.commons.logging.Log;
 33  
 import org.apache.commons.logging.LogFactory;
 34  
 import org.kuali.rice.core.util.ClassLoaderUtils;
 35  
 import org.kuali.rice.kns.bo.BusinessObject;
 36  
 import org.kuali.rice.kns.bo.PersistableBusinessObjectExtension;
 37  
 import org.kuali.rice.kns.datadictionary.exception.AttributeValidationException;
 38  
 import org.kuali.rice.kns.datadictionary.exception.CompletionException;
 39  
 import org.kuali.rice.kns.datadictionary.view.ViewDictionaryIndex;
 40  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 41  
 import org.kuali.rice.kns.service.PersistenceStructureService;
 42  
 import org.kuali.rice.kns.uif.container.View;
 43  
 import org.kuali.rice.kns.uif.util.ComponentIdBeanPostProcessor;
 44  
 import org.kuali.rice.kns.util.ObjectUtils;
 45  
 import org.springframework.beans.factory.config.BeanPostProcessor;
 46  
 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 47  
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 48  
 import org.springframework.context.expression.StandardBeanExpressionResolver;
 49  
 import org.springframework.core.io.DefaultResourceLoader;
 50  
 import org.springframework.core.io.Resource;
 51  
 
 52  
 /**
 53  
  * Collection of named BusinessObjectEntry objects, each of which contains
 54  
  * information relating to the display, validation, and general maintenance of a
 55  
  * BusinessObject.
 56  
  */
 57  0
 public class DataDictionary  {
 58  
 
 59  0
         protected DefaultListableBeanFactory ddBeans = new DefaultListableBeanFactory();
 60  0
     protected XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ddBeans);
 61  
 
 62  
         // logger
 63  0
         private static final Log LOG = LogFactory.getLog(DataDictionary.class);
 64  
 
 65  
         /**
 66  
          * The encapsulation of DataDictionary indices
 67  
          */
 68  0
         protected DataDictionaryIndex ddIndex = new DataDictionaryIndex(ddBeans);
 69  
         
 70  
         // View indices
 71  0
         protected ViewDictionaryIndex viewIndex = new ViewDictionaryIndex(ddBeans);
 72  
 
 73  
         /**
 74  
          * The DataDictionaryMapper
 75  
          * The default mapper simply consults the initialized indices
 76  
          * on workflow document type
 77  
          */
 78  0
         protected DataDictionaryMapper ddMapper = new DataDictionaryIndexMapper();
 79  
 
 80  0
         protected List<String> configFileLocations = new ArrayList<String>();
 81  
         
 82  
 
 83  
         public List<String> getConfigFileLocations() {
 84  0
         return this.configFileLocations;
 85  
     }
 86  
 
 87  
     public void setConfigFileLocations(List<String> configFileLocations) {
 88  0
         this.configFileLocations = configFileLocations;
 89  0
     }
 90  
     
 91  
     public void addConfigFileLocation( String location ) throws IOException {
 92  0
         indexSource( location );
 93  0
     }
 94  
 
 95  
     /**
 96  
      * Sets the DataDictionaryMapper
 97  
      * @param mapper the datadictionary mapper
 98  
      */
 99  
     public void setDataDictionaryMapper(DataDictionaryMapper mapper) {
 100  0
             this.ddMapper = mapper;
 101  0
     }
 102  
     
 103  
     private void indexSource(String sourceName) throws IOException {        
 104  0
         if (sourceName == null) {
 105  0
             throw new DataDictionaryException("Source Name given is null");
 106  
         }
 107  
 
 108  0
         if (!sourceName.endsWith(".xml") ) {
 109  0
             Resource resource = getFileResource(sourceName);
 110  0
             if (resource.exists()) {
 111  0
                 indexSource(resource.getFile());
 112  
             } else {
 113  0
                 LOG.warn("Could not find " + sourceName);
 114  0
                 throw new DataDictionaryException("DD Resource " + sourceName + " not found");
 115  
             }
 116  0
         } else {
 117  0
             if ( LOG.isDebugEnabled() ) {
 118  0
                 LOG.debug("adding sourceName " + sourceName + " ");
 119  
             }
 120  0
             Resource resource = getFileResource(sourceName);
 121  0
             if (! resource.exists()) {
 122  0
                 throw new DataDictionaryException("DD Resource " + sourceName + " not found");  
 123  
             }
 124  
             
 125  0
             String indexName = sourceName.substring(sourceName.lastIndexOf("/") + 1, sourceName.indexOf(".xml"));
 126  0
             configFileLocations.add( sourceName );
 127  
         }
 128  0
     }    
 129  
 
 130  
     protected Resource getFileResource(String sourceName) {
 131  0
         DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
 132  0
         return resourceLoader.getResource(sourceName);
 133  
     }
 134  
 
 135  
     private void indexSource(File dir) {
 136  0
         for (File file : dir.listFiles()) {
 137  0
             if (file.isDirectory()) {
 138  0
                 indexSource(file);
 139  0
             } else if (file.getName().endsWith(".xml") ) {
 140  0
                 configFileLocations.add( "file:" + file.getAbsolutePath());
 141  
             } else {
 142  0
                 if ( LOG.isDebugEnabled() ) {
 143  0
                     LOG.debug("Skipping non xml file " + file.getAbsolutePath() + " in DD load");
 144  
                 }
 145  
             }
 146  
         }
 147  0
     }
 148  
     
 149  
     public void parseDataDictionaryConfigurationFiles( boolean allowConcurrentValidation ) {
 150  
                 // configure the bean factory, setup component decorator post processor
 151  
                 // and allow Spring EL
 152  
                 try {
 153  0
                         BeanPostProcessor idPostProcessor = ComponentIdBeanPostProcessor.class.newInstance();
 154  0
                         ddBeans.addBeanPostProcessor(idPostProcessor);
 155  
                         
 156  0
                         ddBeans.setBeanExpressionResolver(new StandardBeanExpressionResolver());
 157  
                 }
 158  0
                 catch (Exception e1) {
 159  0
                         LOG.error("Cannot create component decorator post processor: " + e1.getMessage(), e1);
 160  0
                         throw new RuntimeException("Cannot create component decorator post processor: " + e1.getMessage(), e1);
 161  0
                 }
 162  
             
 163  
         // expand configuration locations into files
 164  0
         LOG.info( "Starting DD XML File Load" );
 165  
         
 166  0
         String[] configFileLocationsArray = new String[configFileLocations.size()];
 167  0
         configFileLocationsArray = configFileLocations.toArray( configFileLocationsArray );
 168  0
         configFileLocations.clear(); // empty the list out so other items can be added
 169  
         try {
 170  0
             xmlReader.loadBeanDefinitions( configFileLocationsArray );
 171  0
         } catch (Exception e) {
 172  0
             LOG.error("Error loading bean definitions", e);
 173  0
             throw new DataDictionaryException("Error loading bean definitions: " + e.getLocalizedMessage());
 174  0
         }
 175  0
         LOG.info( "Completed DD XML File Load" );
 176  0
         if ( allowConcurrentValidation ) {
 177  0
             Thread t = new Thread(ddIndex);
 178  0
             t.start();
 179  
             
 180  0
             Thread t2 = new Thread(viewIndex);
 181  0
             t2.start();   
 182  0
         } else {
 183  0
             ddIndex.run();
 184  0
             viewIndex.run();
 185  
         }
 186  0
     }
 187  
 
 188  0
         static boolean validateEBOs = true;
 189  
     
 190  
     public void validateDD( boolean validateEbos ) {
 191  0
             DataDictionary.validateEBOs = validateEbos;
 192  0
             Map<String,DataObjectEntry> doBeans = ddBeans.getBeansOfType(DataObjectEntry.class);
 193  0
         for ( DataObjectEntry entry : doBeans.values() ) {
 194  0
             entry.completeValidation();
 195  
         }
 196  0
         Map<String,DocumentEntry> docBeans = ddBeans.getBeansOfType(DocumentEntry.class);
 197  0
         for ( DocumentEntry entry : docBeans.values() ) {
 198  0
             entry.completeValidation();
 199  
         }
 200  0
     }
 201  
     
 202  
     public void validateDD() {
 203  0
             validateDD(true);
 204  0
     }
 205  
 
 206  
         /**
 207  
          * @param className
 208  
          * @return BusinessObjectEntry for the named class, or null if none exists
 209  
          */
 210  
     @Deprecated
 211  
         public BusinessObjectEntry getBusinessObjectEntry(String className ) {
 212  0
                 return ddMapper.getBusinessObjectEntry(ddIndex, className);
 213  
         }
 214  
 
 215  
         /**
 216  
      * @param className
 217  
      * @return BusinessObjectEntry for the named class, or null if none exists
 218  
      */
 219  
     public DataObjectEntry getDataObjectEntry(String className ) {
 220  0
         return ddMapper.getDataObjectEntry(ddIndex, className);
 221  
     }
 222  
 
 223  
         /**
 224  
          * This method gets the business object entry for a concrete class
 225  
          * 
 226  
          * @param className
 227  
          * @return
 228  
          */
 229  
         public BusinessObjectEntry getBusinessObjectEntryForConcreteClass(String className){
 230  0
                 return ddMapper.getBusinessObjectEntryForConcreteClass(ddIndex, className);
 231  
         }
 232  
         
 233  
         /**
 234  
          * @return List of businessObject classnames
 235  
          */
 236  
         public List<String> getBusinessObjectClassNames() {
 237  0
                 return ddMapper.getBusinessObjectClassNames(ddIndex);
 238  
         }
 239  
 
 240  
         /**
 241  
          * @return Map of (classname, BusinessObjectEntry) pairs
 242  
          */
 243  
         public Map<String, BusinessObjectEntry> getBusinessObjectEntries() {
 244  0
                 return ddMapper.getBusinessObjectEntries(ddIndex);
 245  
         }
 246  
 
 247  
         /**
 248  
          * @param className
 249  
          * @return DataDictionaryEntryBase for the named class, or null if none
 250  
          *         exists
 251  
          */
 252  
         public DataDictionaryEntry getDictionaryObjectEntry(String className) {
 253  0
                 return ddMapper.getDictionaryObjectEntry(ddIndex, className);
 254  
         }
 255  
 
 256  
         /**
 257  
          * Returns the KNS document entry for the given lookup key.  The documentTypeDDKey is interpreted
 258  
          * successively in the following ways until a mapping is found (or none if found):
 259  
          * <ol>
 260  
          * <li>KEW/workflow document type</li>
 261  
          * <li>business object class name</li>
 262  
          * <li>maintainable class name</li>
 263  
          * </ol>
 264  
          * This mapping is compiled when DataDictionary files are parsed on startup (or demand).  Currently this
 265  
          * means the mapping is static, and one-to-one (one KNS document maps directly to one and only
 266  
          * one key).
 267  
          * 
 268  
          * @param documentTypeDDKey the KEW/workflow document type name
 269  
          * @return the KNS DocumentEntry if it exists
 270  
          */
 271  
         public DocumentEntry getDocumentEntry(String documentTypeDDKey ) {
 272  0
                 return ddMapper.getDocumentEntry(ddIndex, documentTypeDDKey);
 273  
         }
 274  
 
 275  
         /**
 276  
          * Note: only MaintenanceDocuments are indexed by businessObject Class
 277  
          * 
 278  
          * This is a special case that is referenced in one location. Do we need
 279  
          * another map for this stuff??
 280  
          * 
 281  
          * @param businessObjectClass
 282  
          * @return DocumentEntry associated with the given Class, or null if there
 283  
          *         is none
 284  
          */
 285  
         public MaintenanceDocumentEntry getMaintenanceDocumentEntryForBusinessObjectClass(Class<?> businessObjectClass) {
 286  0
                 return ddMapper.getMaintenanceDocumentEntryForBusinessObjectClass(ddIndex, businessObjectClass);
 287  
         }
 288  
 
 289  
         public Map<String, DocumentEntry> getDocumentEntries() {
 290  0
                 return ddMapper.getDocumentEntries(ddIndex);
 291  
         }
 292  
         
 293  
         /**
 294  
          * Returns the View entry identified by the given id
 295  
          * 
 296  
          * @param viewId - unique id for view
 297  
          * @return View instance associated with the id
 298  
          */
 299  
         public View getViewById(String viewId) {
 300  0
                 return ddMapper.getViewById(viewIndex, viewId);
 301  
         }
 302  
         
 303  
         /**
 304  
          * Returns View instance identified by the view type name and index
 305  
          * 
 306  
          * @param viewTypeName
 307  
          *            - type name for the view
 308  
          * @param indexKey
 309  
          *            - Map of index key parameters, these are the parameters the
 310  
          *            indexer used to index the view initially and needs to identify
 311  
          *            an unique view instance
 312  
          * @return View instance that matches the given index
 313  
          */
 314  
         public View getViewByTypeIndex(String viewTypeName, Map<String, String> indexKey) {
 315  0
                 return ddMapper.getViewByTypeIndex(viewIndex, viewTypeName, indexKey);
 316  
         }
 317  
         
 318  
         /**
 319  
          * Gets all <code>View</code> prototypes configured for the given view type
 320  
          * name
 321  
          * 
 322  
          * @param viewTypeName
 323  
          *            - view type name to retrieve
 324  
          * @return List<View> view prototypes with the given type name, or empty
 325  
          *         list
 326  
          */
 327  
         public List<View> getViewsForType(String viewTypeName) {
 328  0
                 return ddMapper.getViewsForType(viewIndex, viewTypeName);
 329  
         }
 330  
 
 331  
     /**
 332  
      * @param clazz
 333  
      * @param propertyName
 334  
      * @return true if the given propertyName names a property of the given class
 335  
      * @throws CompletionException if there is a problem accessing the named property on the given class
 336  
      */
 337  
     public static boolean isPropertyOf(Class targetClass, String propertyName) {
 338  0
         if (targetClass == null) {
 339  0
             throw new IllegalArgumentException("invalid (null) targetClass");
 340  
         }
 341  0
         if (StringUtils.isBlank(propertyName)) {
 342  0
             throw new IllegalArgumentException("invalid (blank) propertyName");
 343  
         }
 344  
 
 345  0
         PropertyDescriptor propertyDescriptor = buildReadDescriptor(targetClass, propertyName);
 346  
 
 347  0
         boolean isPropertyOf = (propertyDescriptor != null);
 348  0
         return isPropertyOf;
 349  
     }
 350  
 
 351  
     /**
 352  
      * @param clazz
 353  
      * @param propertyName
 354  
      * @return true if the given propertyName names a Collection property of the given class
 355  
      * @throws CompletionException if there is a problem accessing the named property on the given class
 356  
      */
 357  
     public static boolean isCollectionPropertyOf(Class targetClass, String propertyName) {
 358  0
         boolean isCollectionPropertyOf = false;
 359  
 
 360  0
         PropertyDescriptor propertyDescriptor = buildReadDescriptor(targetClass, propertyName);
 361  0
         if (propertyDescriptor != null) {
 362  0
             Class clazz = propertyDescriptor.getPropertyType();
 363  
 
 364  0
             if ((clazz != null) && Collection.class.isAssignableFrom(clazz)) {
 365  0
                 isCollectionPropertyOf = true;
 366  
             }
 367  
         }
 368  
 
 369  0
         return isCollectionPropertyOf;
 370  
     }
 371  
 
 372  
     public static PersistenceStructureService persistenceStructureService;
 373  
     
 374  
     /**
 375  
      * @return the persistenceStructureService
 376  
      */
 377  
     public static PersistenceStructureService getPersistenceStructureService() {
 378  0
         if ( persistenceStructureService == null ) {
 379  0
             persistenceStructureService = KNSServiceLocator.getPersistenceStructureService();
 380  
         }
 381  0
         return persistenceStructureService;
 382  
     }
 383  
     
 384  
     /**
 385  
      * This method determines the Class of the attributeName passed in. Null will be returned if the member is not available, or if
 386  
      * a reflection exception is thrown.
 387  
      * 
 388  
      * @param rootClass - Class that the attributeName property exists in.
 389  
      * @param attributeName - Name of the attribute you want a class for.
 390  
      * @return The Class of the attributeName, if the attribute exists on the rootClass. Null otherwise.
 391  
      */
 392  
     public static Class getAttributeClass(Class boClass, String attributeName) {
 393  
 
 394  
         // fail loudly if the attributeName isnt a member of rootClass
 395  0
         if (!isPropertyOf(boClass, attributeName)) {
 396  0
             throw new AttributeValidationException("unable to find attribute '" + attributeName + "' in rootClass '" + boClass.getName() + "'");
 397  
         }
 398  
 
 399  
             //Implementing Externalizable Business Object Services...
 400  
         //The boClass can be an interface, hence handling this separately, 
 401  
         //since the original method was throwing exception if the class could not be instantiated.
 402  0
         if(boClass.isInterface())
 403  0
                 return getAttributeClassWhenBOIsInterface(boClass, attributeName);
 404  
         else
 405  0
                 return getAttributeClassWhenBOIsClass(boClass, attributeName);                
 406  
 
 407  
     }
 408  
 
 409  
     /**
 410  
      * 
 411  
      * This method gets the property type of the given attributeName when the bo class is a concrete class
 412  
      * 
 413  
      * @param boClass
 414  
      * @param attributeName
 415  
      * @return
 416  
      */
 417  
     private static Class getAttributeClassWhenBOIsClass(Class boClass, String attributeName){
 418  
             BusinessObject boInstance;
 419  
         try {
 420  0
             boInstance = (BusinessObject) boClass.newInstance();
 421  0
         } catch (Exception e) {
 422  0
                 throw new RuntimeException("Unable to instantiate BO: " + boClass, e);
 423  0
         }
 424  
 
 425  
         // attempt to retrieve the class of the property
 426  
         try {
 427  0
             return ObjectUtils.getPropertyType(boInstance, attributeName, getPersistenceStructureService());
 428  0
         } catch (Exception e) {
 429  0
             throw new RuntimeException("Unable to determine property type for: " + boClass.getName() + "." + attributeName, e);
 430  
         }
 431  
     }
 432  
 
 433  
     /**
 434  
      * 
 435  
      * This method gets the property type of the given attributeName when the bo class is an interface
 436  
      * This method will also work if the bo class is not an interface, 
 437  
      * but that case requires special handling, hence a separate method getAttributeClassWhenBOIsClass 
 438  
      * 
 439  
      * @param boClass
 440  
      * @param attributeName
 441  
      * @return
 442  
      */
 443  
     private static Class getAttributeClassWhenBOIsInterface(Class boClass, String attributeName){
 444  0
         if (boClass == null) {
 445  0
             throw new IllegalArgumentException("invalid (null) boClass");
 446  
         }
 447  0
         if (StringUtils.isBlank(attributeName)) {
 448  0
             throw new IllegalArgumentException("invalid (blank) attributeName");
 449  
         }
 450  
 
 451  0
         PropertyDescriptor propertyDescriptor = null;
 452  
 
 453  0
         String[] intermediateProperties = attributeName.split("\\.");
 454  0
         int lastLevel = intermediateProperties.length - 1;
 455  0
         Class currentClass = boClass;
 456  
 
 457  0
         for (int i = 0; i <= lastLevel; ++i) {
 458  
 
 459  0
             String currentPropertyName = intermediateProperties[i];
 460  0
             propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
 461  
 
 462  0
             if (propertyDescriptor != null) {
 463  
 
 464  0
                 Class propertyType = propertyDescriptor.getPropertyType();
 465  0
                 if ( propertyType.equals( PersistableBusinessObjectExtension.class ) ) {
 466  0
                     propertyType = getPersistenceStructureService().getBusinessObjectAttributeClass( currentClass, currentPropertyName );                    
 467  
                 }
 468  0
                 if (Collection.class.isAssignableFrom(propertyType)) {
 469  
                         // TODO: determine property type using generics type definition
 470  0
                         throw new AttributeValidationException("Can't determine the Class of Collection elements because when the business object is an (possibly ExternalizableBusinessObject) interface.");
 471  
                 }
 472  
                 else {
 473  0
                     currentClass = propertyType;
 474  
                 }
 475  0
             }
 476  
             else {
 477  0
                     throw new AttributeValidationException("Can't find getter method of " + boClass.getName() + " for property " + attributeName);
 478  
             }
 479  
         }
 480  0
         return currentClass;
 481  
     }
 482  
     
 483  
     /**
 484  
      * This method determines the Class of the elements in the collectionName passed in.
 485  
      * 
 486  
      * @param boClass Class that the collectionName collection exists in.
 487  
      * @param collectionName the name of the collection you want the element class for
 488  
      * @return
 489  
      */
 490  
     public static Class getCollectionElementClass(Class boClass, String collectionName) {
 491  0
         if (boClass == null) {
 492  0
             throw new IllegalArgumentException("invalid (null) boClass");
 493  
         }
 494  0
         if (StringUtils.isBlank(collectionName)) {
 495  0
             throw new IllegalArgumentException("invalid (blank) collectionName");
 496  
         }
 497  
 
 498  0
         PropertyDescriptor propertyDescriptor = null;
 499  
 
 500  0
         String[] intermediateProperties = collectionName.split("\\.");
 501  0
         Class currentClass = boClass;
 502  
 
 503  0
         for (int i = 0; i <intermediateProperties.length; ++i) {
 504  
 
 505  0
             String currentPropertyName = intermediateProperties[i];
 506  0
             propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
 507  
 
 508  
 
 509  0
                 if (propertyDescriptor != null) {
 510  
 
 511  0
                     Class type = propertyDescriptor.getPropertyType();
 512  0
                     if (Collection.class.isAssignableFrom(type)) {
 513  
 
 514  0
                         if (getPersistenceStructureService().isPersistable(currentClass)) {
 515  
 
 516  0
                             Map<String, Class> collectionClasses = new HashMap<String, Class>();
 517  0
                             collectionClasses = getPersistenceStructureService().listCollectionObjectTypes(currentClass);
 518  0
                             currentClass = collectionClasses.get(currentPropertyName);
 519  
 
 520  0
                         }
 521  
                         else {
 522  0
                             throw new RuntimeException("Can't determine the Class of Collection elements because persistenceStructureService.isPersistable(" + currentClass.getName() + ") returns false.");
 523  
                         }
 524  
 
 525  
                     }
 526  
                     else {
 527  
 
 528  0
                         currentClass = propertyDescriptor.getPropertyType();
 529  
 
 530  
                     }
 531  
                 }
 532  
             }
 533  
 
 534  0
         return currentClass;
 535  
     }
 536  
 
 537  0
     static private Map<String, Map<String, PropertyDescriptor>> cache = new TreeMap<String, Map<String, PropertyDescriptor>>();
 538  
 
 539  
     /**
 540  
      * @param propertyClass
 541  
      * @param propertyName
 542  
      * @return PropertyDescriptor for the getter for the named property of the given class, if one exists.
 543  
      */
 544  
     public static PropertyDescriptor buildReadDescriptor(Class propertyClass, String propertyName) {
 545  0
         if (propertyClass == null) {
 546  0
             throw new IllegalArgumentException("invalid (null) propertyClass");
 547  
         }
 548  0
         if (StringUtils.isBlank(propertyName)) {
 549  0
             throw new IllegalArgumentException("invalid (blank) propertyName");
 550  
         }
 551  
 
 552  0
         PropertyDescriptor propertyDescriptor = null;
 553  
 
 554  0
         String[] intermediateProperties = propertyName.split("\\.");
 555  0
         int lastLevel = intermediateProperties.length - 1;
 556  0
         Class currentClass = propertyClass;
 557  
 
 558  0
         for (int i = 0; i <= lastLevel; ++i) {
 559  
 
 560  0
             String currentPropertyName = intermediateProperties[i];
 561  0
             propertyDescriptor = buildSimpleReadDescriptor(currentClass, currentPropertyName);
 562  
 
 563  0
             if (i < lastLevel) {
 564  
 
 565  0
                 if (propertyDescriptor != null) {
 566  
 
 567  0
                     Class propertyType = propertyDescriptor.getPropertyType();
 568  0
                     if ( propertyType.equals( PersistableBusinessObjectExtension.class ) ) {
 569  0
                         propertyType = getPersistenceStructureService().getBusinessObjectAttributeClass( currentClass, currentPropertyName );                    
 570  
                     }
 571  0
                     if (Collection.class.isAssignableFrom(propertyType)) {
 572  
 
 573  0
                         if (getPersistenceStructureService().isPersistable(currentClass)) {
 574  
 
 575  0
                             Map<String, Class> collectionClasses = new HashMap<String, Class>();
 576  0
                             collectionClasses = getPersistenceStructureService().listCollectionObjectTypes(currentClass);
 577  0
                             currentClass = collectionClasses.get(currentPropertyName);
 578  
 
 579  0
                         }
 580  
                         else {
 581  
 
 582  0
                             throw new RuntimeException("Can't determine the Class of Collection elements because persistenceStructureService.isPersistable(" + currentClass.getName() + ") returns false.");
 583  
 
 584  
                         }
 585  
 
 586  
                     }
 587  
                     else {
 588  
 
 589  0
                         currentClass = propertyType;
 590  
 
 591  
                     }
 592  
 
 593  
                 }
 594  
 
 595  
             }
 596  
 
 597  
         }
 598  
 
 599  0
         return propertyDescriptor;
 600  
     }
 601  
 
 602  
     /**
 603  
      * @param propertyClass
 604  
      * @param propertyName
 605  
      * @return PropertyDescriptor for the getter for the named property of the given class, if one exists.
 606  
      */
 607  
     public static PropertyDescriptor buildSimpleReadDescriptor(Class propertyClass, String propertyName) {
 608  0
         if (propertyClass == null) {
 609  0
             throw new IllegalArgumentException("invalid (null) propertyClass");
 610  
         }
 611  0
         if (StringUtils.isBlank(propertyName)) {
 612  0
             throw new IllegalArgumentException("invalid (blank) propertyName");
 613  
         }
 614  
 
 615  0
         PropertyDescriptor p = null;
 616  
 
 617  
         // check to see if we've cached this descriptor already. if yes, return true.
 618  0
         String propertyClassName = propertyClass.getName();
 619  0
         Map<String, PropertyDescriptor> m = cache.get(propertyClassName);
 620  0
         if (null != m) {
 621  0
             p = m.get(propertyName);
 622  0
             if (null != p) {
 623  0
                 return p;
 624  
             }
 625  
         }
 626  
 
 627  0
         String prefix = StringUtils.capitalize(propertyName);
 628  0
         String getName = "get" + prefix;
 629  0
         String isName = "is" + prefix;
 630  
 
 631  
         try {
 632  
 
 633  0
             p = new PropertyDescriptor(propertyName, propertyClass, getName, null);
 634  
 
 635  
         }
 636  0
         catch (IntrospectionException e) {
 637  
             try {
 638  
 
 639  0
                 p = new PropertyDescriptor(propertyName, propertyClass, isName, null);
 640  
 
 641  
             }
 642  0
             catch (IntrospectionException f) {
 643  
                 // ignore it
 644  0
             }
 645  0
         }
 646  
 
 647  
         // cache the property descriptor if we found it.
 648  0
         if (null != p) {
 649  
 
 650  0
             if (null == m) {
 651  
 
 652  0
                 m = new TreeMap<String, PropertyDescriptor>();
 653  0
                 cache.put(propertyClassName, m);
 654  
 
 655  
             }
 656  
 
 657  0
             m.put(propertyName, p);
 658  
 
 659  
         }
 660  
 
 661  0
         return p;
 662  
     }
 663  
 
 664  
     public Set<InactivationBlockingMetadata> getAllInactivationBlockingMetadatas(Class blockedClass) {
 665  0
             return ddMapper.getAllInactivationBlockingMetadatas(ddIndex, blockedClass);
 666  
     }
 667  
     
 668  
     /**
 669  
      * This method gathers beans of type BeanOverride and invokes each one's performOverride() method.
 670  
      */
 671  
     // KULRICE-4513
 672  
     public void performBeanOverrides()
 673  
     {
 674  0
             Collection<BeanOverride> beanOverrides = ddBeans.getBeansOfType(BeanOverride.class).values();
 675  
             
 676  0
             if (beanOverrides.isEmpty()){
 677  0
                     LOG.info("DataDictionary.performOverrides(): No beans to override");
 678  
             }
 679  0
                 for (BeanOverride beanOverride : beanOverrides) {
 680  
                         
 681  0
                         Object bean = ddBeans.getBean(beanOverride.getBeanName());
 682  0
                         beanOverride.performOverride(bean);
 683  0
                         LOG.info("DataDictionary.performOverrides(): Performing override on bean: " + bean.toString());
 684  0
                 }
 685  0
     }
 686  
 }