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