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