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