View Javadoc

1   /*
2    * Copyright 2011 The Kuali Foundation Licensed under the Educational Community
3    * License, Version 1.0 (the "License"); you may not use this file except in
4    * compliance with the License. You may obtain a copy of the License at
5    * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
6    * or agreed to in writing, software distributed under the License is
7    * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8    * KIND, either express or implied. See the License for the specific language
9    * governing permissions and limitations under the License.
10   */
11  package org.kuali.rice.krad.service.impl;
12  
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.TreeMap;
19  
20  import org.apache.commons.lang.StringUtils;
21  import org.kuali.rice.krad.bo.BusinessObject;
22  import org.kuali.rice.krad.bo.BusinessObjectRelationship;
23  import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
24  import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
25  import org.kuali.rice.krad.datadictionary.DataObjectEntry;
26  import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
27  import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
28  import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
29  import org.kuali.rice.krad.service.DataDictionaryService;
30  import org.kuali.rice.krad.service.DataObjectMetaDataService;
31  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
32  import org.kuali.rice.krad.service.KualiModuleService;
33  import org.kuali.rice.krad.service.ModuleService;
34  import org.kuali.rice.krad.service.PersistenceStructureService;
35  import org.kuali.rice.krad.uif.service.ViewDictionaryService;
36  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
37  import org.kuali.rice.krad.util.ObjectUtils;
38  import org.springframework.beans.BeanWrapper;
39  
40  /**
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class DataObjectMetaDataServiceImpl implements DataObjectMetaDataService {
44  
45      private DataDictionaryService dataDictionaryService;
46      private KualiModuleService kualiModuleService;
47      private PersistenceStructureService persistenceStructureService;
48      private ViewDictionaryService viewDictionaryService;
49  
50      /**
51       * @see org.kuali.rice.krad.service.DataObjectMetaDataService#listPrimaryKeyFieldNames(java.lang.Class)
52       */
53      @Override
54      public List<String> listPrimaryKeyFieldNames(Class<?> clazz) {
55          if (persistenceStructureService.isPersistable(clazz)) {
56              return persistenceStructureService.listPrimaryKeyFieldNames(clazz);
57          }
58  
59          ModuleService responsibleModuleService = getKualiModuleService().getResponsibleModuleService(clazz);
60          if (responsibleModuleService != null && responsibleModuleService.isExternalizable(clazz))
61              return responsibleModuleService.listPrimaryKeyFieldNames(clazz);
62  
63          // check the Data Dictionary for PK's of non PBO/EBO
64          List<String> pks = dataDictionaryService.getDataDictionary().getDataObjectEntry(clazz.getName())
65                  .getPrimaryKeys();
66          if (pks != null && !pks.isEmpty())
67              return pks;
68  
69          return new ArrayList<String>();
70      }
71  
72      /**
73       * @see org.kuali.rice.krad.service.DataObjectMetaDataService#getPrimaryKeyFieldValues(java.lang.Object)
74       */
75      @Override
76      public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject) {
77          return getPrimaryKeyFieldValues(dataObject, false);
78      }
79  
80      /**
81       * @see org.kuali.rice.krad.service.DataObjectMetaDataService#getPrimaryKeyFieldValues(java.lang.Object,
82       *      boolean)
83       */
84      @Override
85      public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject, boolean sortFieldNames) {
86          Map<String, Object> keyValueMap;
87  
88          if (sortFieldNames) {
89              keyValueMap = new TreeMap<String, Object>();
90          } else {
91              keyValueMap = new HashMap<String, Object>();
92          }
93  
94          BeanWrapper wrapper = ObjectPropertyUtils.wrapObject(dataObject);
95  
96          List<String> fields = listPrimaryKeyFieldNames(dataObject.getClass());
97          for (String fieldName : fields) {
98              keyValueMap.put(fieldName, wrapper.getPropertyValue(fieldName));
99          }
100 
101         return keyValueMap;
102     }
103 
104     /**
105      * @see org.kuali.rice.krad.service.DataObjectMetaDataService#equalsByPrimaryKeys(java.lang.Object,
106      *      java.lang.Object)
107      */
108     @Override
109     public boolean equalsByPrimaryKeys(Object do1, Object do2) {
110         boolean equal = true;
111 
112         if (do1 == null && do2 == null) {
113             equal = true;
114         } else if (do1 == null || do2 == null) {
115             equal = false;
116         } else if (!do1.getClass().getName().equals(do2.getClass().getName())) {
117             equal = false;
118         } else {
119             Map<String, ?> do1Keys = getPrimaryKeyFieldValues(do1);
120             Map<String, ?> do2Keys = getPrimaryKeyFieldValues(do2);
121             for (Iterator<String> iter = do1Keys.keySet().iterator(); iter.hasNext();) {
122                 String keyName = iter.next();
123                 if (do1Keys.get(keyName) != null && do2Keys.get(keyName) != null) {
124                     if (!do1Keys.get(keyName).toString().equals(do2Keys.get(keyName).toString())) {
125                         equal = false;
126                     }
127                 } else {
128                     equal = false;
129                 }
130             }
131         }
132 
133         return equal;
134     }
135 
136     /**
137      * @see org.kuali.rice.kns.service.BusinessObjectMetaDataService#getDataObjectRelationship(java.lang.Object,
138      *      java.lang.Class, java.lang.String, java.lang.String, boolean,
139      *      boolean, boolean)
140      */
141     public BusinessObjectRelationship getDataObjectRelationship(Object dataObject, Class<?> dataObjectClass,
142             String attributeName, String attributePrefix, boolean keysOnly, boolean supportsLookup,
143             boolean supportsInquiry) {
144         RelationshipDefinition ddReference = getDictionaryRelationship(dataObjectClass, attributeName);
145 
146         return getDataObjectRelationship(ddReference, dataObject, dataObjectClass, attributeName, attributePrefix,
147                 keysOnly, supportsLookup, supportsInquiry);
148     }
149 
150     protected BusinessObjectRelationship getDataObjectRelationship(RelationshipDefinition ddReference,
151             Object dataObject, Class<?> dataObjectClass, String attributeName, String attributePrefix,
152             boolean keysOnly, boolean supportsLookup, boolean supportsInquiry) {
153         BusinessObjectRelationship relationship = null;
154 
155         // if it is nested then replace the bo and attributeName with the
156         // sub-refs
157         if (ObjectUtils.isNestedAttribute(attributeName)) {
158             if (ddReference != null) {
159                 if ((supportsLookup && getViewDictionaryService().isLookupable(ddReference.getTargetClass()))
160                         || (supportsInquiry && getViewDictionaryService().isInquirable(ddReference.getTargetClass()))) {
161                     relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference,
162                             attributePrefix, keysOnly);
163 
164                     return relationship;
165                 }
166             }
167 
168             // recurse down to the next object to find the relationship
169             String localPrefix = StringUtils.substringBefore(attributeName, ".");
170             String localAttributeName = StringUtils.substringAfter(attributeName, ".");
171             if (dataObject == null) {
172                 dataObject = ObjectUtils.createNewObjectFromClass(dataObjectClass);
173             }
174 
175             Object nestedObject = ObjectPropertyUtils.getPropertyValue(dataObject, localPrefix);
176             Class<?> nestedClass = null;
177             if (nestedObject == null) {
178                 nestedClass = ObjectPropertyUtils.getPropertyType(dataObject, localPrefix);
179             }
180             else {
181                 nestedClass = nestedObject.getClass();
182             }
183             
184             String fullPrefix = localPrefix;
185             if (StringUtils.isNotBlank(attributePrefix)) {
186                 fullPrefix = attributePrefix + "." + localPrefix;
187             }
188 
189             relationship = getDataObjectRelationship(nestedObject, nestedClass, localAttributeName, fullPrefix, keysOnly,
190                     supportsLookup, supportsInquiry);
191 
192             return relationship;
193         }
194 
195         // non-nested reference, get persistence relationships first
196         int maxSize = Integer.MAX_VALUE;
197 
198         // try persistable reference first
199         if (getPersistenceStructureService().isPersistable(dataObjectClass)) {
200             Map<String, BusinessObjectRelationship> rels = getPersistenceStructureService().getRelationshipMetadata(
201                     dataObjectClass, attributeName, attributePrefix);
202             if (rels.size() > 0) {
203                 for (BusinessObjectRelationship rel : rels.values()) {
204                     if (rel.getParentToChildReferences().size() < maxSize) {
205                         if ((supportsLookup && getViewDictionaryService().isLookupable(rel.getRelatedClass()))
206                                 || (supportsInquiry && getViewDictionaryService().isInquirable(rel.getRelatedClass()))) {
207                             maxSize = rel.getParentToChildReferences().size();
208                             relationship = rel;
209                         }
210                     }
211                 }
212             }
213         } else {
214             ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(dataObjectClass);
215             if (moduleService != null && moduleService.isExternalizable(dataObjectClass)) {
216                 relationship = getRelationshipMetadata(dataObjectClass, attributeName, attributePrefix);
217                 if (relationship != null) {
218                     return relationship;
219                 }
220             }
221         }
222 
223         if (ddReference != null && ddReference.getPrimitiveAttributes().size() < maxSize) {
224             if ((supportsLookup && getViewDictionaryService().isLookupable(ddReference.getTargetClass()))
225                     || (supportsInquiry && getViewDictionaryService().isInquirable(ddReference.getTargetClass()))) {
226                 relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference, null, keysOnly);
227             }
228         }
229 
230         return relationship;
231     }
232 
233     public RelationshipDefinition getDictionaryRelationship(Class<?> c, String attributeName) {
234         DataDictionaryEntry entryBase = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(
235                 c.getName());
236         if (entryBase == null) {
237             return null;
238         }
239 
240         RelationshipDefinition relationship = null;
241 
242         List<RelationshipDefinition> ddRelationships = entryBase.getRelationships();
243 
244         int minKeys = Integer.MAX_VALUE;
245         for (RelationshipDefinition def : ddRelationships) {
246             // favor key sizes of 1 first
247             if (def.getPrimitiveAttributes().size() == 1) {
248                 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) {
249                     if (primitive.getSourceName().equals(attributeName)
250                             || def.getObjectAttributeName().equals(attributeName)) {
251                         relationship = def;
252                         minKeys = 1;
253                         break;
254                     }
255                 }
256             } else if (def.getPrimitiveAttributes().size() < minKeys) {
257                 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) {
258                     if (primitive.getSourceName().equals(attributeName)
259                             || def.getObjectAttributeName().equals(attributeName)) {
260                         relationship = def;
261                         minKeys = def.getPrimitiveAttributes().size();
262                         break;
263                     }
264                 }
265             }
266         }
267         // check the support attributes
268         if (relationship == null) {
269             for (RelationshipDefinition def : ddRelationships) {
270                 if (def.hasIdentifier()) {
271                     if (def.getIdentifier().getSourceName().equals(attributeName)) {
272                         relationship = def;
273                     }
274                 }
275             }
276         }
277 
278         return relationship;
279     }
280 
281     protected BusinessObjectRelationship populateRelationshipFromDictionaryReference(Class<?> dataObjectClass,
282             RelationshipDefinition ddReference, String attributePrefix, boolean keysOnly) {
283         BusinessObjectRelationship relationship = new BusinessObjectRelationship(dataObjectClass,
284                 ddReference.getObjectAttributeName(), ddReference.getTargetClass());
285 
286         for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
287             if (StringUtils.isNotBlank(attributePrefix)) {
288                 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
289                         def.getTargetName());
290             } else {
291                 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
292             }
293         }
294 
295         if (!keysOnly) {
296             for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
297                 if (StringUtils.isNotBlank(attributePrefix)) {
298                     relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
299                             def.getTargetName());
300                     if (def.isIdentifier()) {
301                         relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName());
302                     }
303                 } else {
304                     relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
305                     if (def.isIdentifier()) {
306                         relationship.setUserVisibleIdentifierKey(def.getSourceName());
307                     }
308                 }
309             }
310         }
311 
312         return relationship;
313     }
314 
315     protected BusinessObjectRelationship getRelationshipMetadata(Class<?> dataObjectClass, String attributeName,
316             String attributePrefix) {
317 
318         RelationshipDefinition relationshipDefinition = getDictionaryRelationship(dataObjectClass, attributeName);
319         if (relationshipDefinition == null) {
320             return null;
321         }
322 
323         BusinessObjectRelationship businessObjectRelationship = new BusinessObjectRelationship(
324                 relationshipDefinition.getSourceClass(), relationshipDefinition.getObjectAttributeName(),
325                 relationshipDefinition.getTargetClass());
326 
327         if (!StringUtils.isEmpty(attributePrefix)) {
328             attributePrefix += ".";
329         }
330 
331         List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes();
332         for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) {
333             businessObjectRelationship.getParentToChildReferences().put(
334                     attributePrefix + primitiveAttributeDefinition.getSourceName(),
335                     primitiveAttributeDefinition.getTargetName());
336         }
337 
338         return businessObjectRelationship;
339     }
340 
341     /**
342      * @see org.kuali.rice.krad.service.DataObjectMetaDataService#getTitleAttribute(java.lang.Class)
343      */
344     @Override
345     public String getTitleAttribute(Class<?> dataObjectClass) {
346         String titleAttribute = null;
347 
348         DataObjectEntry entry = getDataObjectEntry(dataObjectClass);
349         if (entry != null) {
350             titleAttribute = entry.getTitleAttribute();
351         }
352 
353         return titleAttribute;
354     }
355 
356     /**
357      * @see org.kuali.rice.krad.service.DataObjectMetaDataService#areNotesSupported(java.lang.Class)
358      */
359     @Override
360     public boolean areNotesSupported(Class dataObjectClass) {
361         boolean hasNotesSupport = false;
362 
363         if (BusinessObject.class.isAssignableFrom(dataObjectClass)) {
364             BusinessObjectEntry entry = getBusinessObjectEntry(dataObjectClass);
365             if (entry != null) {
366                 hasNotesSupport = entry.isBoNotesEnabled();
367             }
368         }
369 
370         return hasNotesSupport;
371     }
372     
373     /**
374      * @param dataObjectClass
375      * @return DataObjectEntry for the given dataObjectClass, or null if
376      *         there is none
377      * @throws IllegalArgumentException
378      *             if the given Class is null
379      */
380     protected DataObjectEntry getDataObjectEntry(Class<?> dataObjectClass) {
381         if (dataObjectClass == null) {
382             throw new IllegalArgumentException("invalid (null) dataObjectClass");
383         }
384 
385         DataObjectEntry entry = getDataDictionaryService().getDataDictionary()
386                 .getDataObjectEntry(dataObjectClass.getName());
387         
388         return entry;
389     }
390 
391     /**
392      * @param businessObjectClass - class of business object to return entry for
393      * @return BusinessObjectEntry for the given dataObjectClass, or null if
394      *         there is none
395      */
396     protected BusinessObjectEntry getBusinessObjectEntry(Class businessObjectClass) {
397         validateBusinessObjectClass(businessObjectClass);
398 
399         BusinessObjectEntry entry =
400                 getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(businessObjectClass.getName());
401         return entry;
402     }
403 
404     /**
405      * @param businessObjectClass
406      * @throws IllegalArgumentException if the given Class is null or is not a BusinessObject class
407      */
408     protected void validateBusinessObjectClass(Class businessObjectClass) {
409         if (businessObjectClass == null) {
410             throw new IllegalArgumentException("invalid (null) dataObjectClass");
411         }
412         if (!BusinessObject.class.isAssignableFrom(businessObjectClass)) {
413             throw new IllegalArgumentException(
414                     "class '" + businessObjectClass.getName() + "' is not a descendant of BusinessObject");
415         }
416     }
417 
418     /**
419      * Protected method to allow subclasses to access the dataDictionaryService.
420      * 
421      * @return Returns the dataDictionaryService.
422      */
423     protected DataDictionaryService getDataDictionaryService() {
424         return this.dataDictionaryService;
425     }
426 
427     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
428         this.dataDictionaryService = dataDictionaryService;
429     }
430 
431     /**
432      * Protected method to allow subclasses to access the kualiModuleService.
433      * 
434      * @return Returns the persistenceStructureService.
435      */
436     protected KualiModuleService getKualiModuleService() {
437         return this.kualiModuleService;
438     }
439 
440     public void setKualiModuleService(KualiModuleService kualiModuleService) {
441         this.kualiModuleService = kualiModuleService;
442     }
443 
444     /**
445      * Protected method to allow subclasses to access the
446      * persistenceStructureService.
447      * 
448      * @return Returns the persistenceStructureService.
449      */
450     protected PersistenceStructureService getPersistenceStructureService() {
451         return this.persistenceStructureService;
452     }
453 
454     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
455         this.persistenceStructureService = persistenceStructureService;
456     }
457 
458     protected ViewDictionaryService getViewDictionaryService() {
459         if (this.viewDictionaryService == null) {
460             this.viewDictionaryService = KRADServiceLocatorWeb.getViewDictionaryService();
461         }
462         return this.viewDictionaryService;
463     }
464 
465     public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) {
466         this.viewDictionaryService = viewDictionaryService;
467     }
468 
469 }