1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.service.impl;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.TreeMap;
24 import java.util.UUID;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.kuali.rice.krad.bo.BusinessObject;
28 import org.kuali.rice.krad.bo.DataObjectRelationship;
29 import org.kuali.rice.krad.bo.PersistableBusinessObject;
30 import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
31 import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
32 import org.kuali.rice.krad.datadictionary.DataObjectEntry;
33 import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
34 import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
35 import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition;
36 import org.kuali.rice.krad.service.DataDictionaryService;
37 import org.kuali.rice.krad.service.DataObjectMetaDataService;
38 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
39 import org.kuali.rice.krad.service.KualiModuleService;
40 import org.kuali.rice.krad.service.ModuleService;
41 import org.kuali.rice.krad.service.PersistenceStructureService;
42 import org.kuali.rice.krad.uif.UifPropertyPaths;
43 import org.kuali.rice.krad.uif.service.ViewDictionaryService;
44 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
45 import org.kuali.rice.krad.util.ObjectUtils;
46 import org.springframework.beans.BeanWrapper;
47
48
49
50
51 public class DataObjectMetaDataServiceImpl implements DataObjectMetaDataService {
52
53 private DataDictionaryService dataDictionaryService;
54 private KualiModuleService kualiModuleService;
55 private PersistenceStructureService persistenceStructureService;
56 private ViewDictionaryService viewDictionaryService;
57
58
59
60
61 @Override
62 public List<String> listPrimaryKeyFieldNames(Class<?> clazz) {
63 if (persistenceStructureService.isPersistable(clazz)) {
64 return persistenceStructureService.listPrimaryKeyFieldNames(clazz);
65 }
66
67 ModuleService responsibleModuleService = getKualiModuleService().getResponsibleModuleService(clazz);
68 if (responsibleModuleService != null && responsibleModuleService.isExternalizable(clazz)) {
69 return responsibleModuleService.listPrimaryKeyFieldNames(clazz);
70 }
71
72
73 List<String> pks = dataDictionaryService.getDataDictionary().getDataObjectEntry(clazz.getName())
74 .getPrimaryKeys();
75 if (pks != null && !pks.isEmpty()) {
76 return pks;
77 }
78
79 return new ArrayList<String>();
80 }
81
82
83
84
85 @Override
86 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject) {
87 return getPrimaryKeyFieldValues(dataObject, false);
88 }
89
90
91
92
93
94 @Override
95 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject, boolean sortFieldNames) {
96 Map<String, Object> keyValueMap;
97
98 if (sortFieldNames) {
99 keyValueMap = new TreeMap<String, Object>();
100 } else {
101 keyValueMap = new HashMap<String, Object>();
102 }
103
104 BeanWrapper wrapper = ObjectPropertyUtils.wrapObject(dataObject);
105
106 List<String> fields = listPrimaryKeyFieldNames(dataObject.getClass());
107 for (String fieldName : fields) {
108 keyValueMap.put(fieldName, wrapper.getPropertyValue(fieldName));
109 }
110
111 return keyValueMap;
112 }
113
114
115
116
117
118 @Override
119 public boolean equalsByPrimaryKeys(Object do1, Object do2) {
120 boolean equal = true;
121
122 if (do1 == null && do2 == null) {
123 equal = true;
124 } else if (do1 == null || do2 == null) {
125 equal = false;
126 } else if (!do1.getClass().getName().equals(do2.getClass().getName())) {
127 equal = false;
128 } else {
129 Map<String, ?> do1Keys = getPrimaryKeyFieldValues(do1);
130 Map<String, ?> do2Keys = getPrimaryKeyFieldValues(do2);
131 for (Iterator<String> iter = do1Keys.keySet().iterator(); iter.hasNext(); ) {
132 String keyName = iter.next();
133 if (do1Keys.get(keyName) != null && do2Keys.get(keyName) != null) {
134 if (!do1Keys.get(keyName).toString().equals(do2Keys.get(keyName).toString())) {
135 equal = false;
136 }
137 } else {
138 equal = false;
139 }
140 }
141 }
142
143 return equal;
144 }
145
146
147
148
149
150
151 public DataObjectRelationship getDataObjectRelationship(Object dataObject, Class<?> dataObjectClass,
152 String attributeName, String attributePrefix, boolean keysOnly, boolean supportsLookup,
153 boolean supportsInquiry) {
154 RelationshipDefinition ddReference = getDictionaryRelationship(dataObjectClass, attributeName);
155
156 return getDataObjectRelationship(ddReference, dataObject, dataObjectClass, attributeName, attributePrefix,
157 keysOnly, supportsLookup, supportsInquiry);
158 }
159
160 protected DataObjectRelationship getDataObjectRelationship(RelationshipDefinition ddReference, Object dataObject,
161 Class<?> dataObjectClass, String attributeName, String attributePrefix, boolean keysOnly,
162 boolean supportsLookup, boolean supportsInquiry) {
163 DataObjectRelationship relationship = null;
164
165
166
167 if (ObjectUtils.isNestedAttribute(attributeName)) {
168 if (ddReference != null) {
169 if (classHasSupportedFeatures(ddReference.getTargetClass(), supportsLookup, supportsInquiry)) {
170 relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference,
171 attributePrefix, keysOnly);
172
173 return relationship;
174 }
175 }
176
177
178 String localPrefix = StringUtils.substringBefore(attributeName, ".");
179 String localAttributeName = StringUtils.substringAfter(attributeName, ".");
180 if (dataObject == null) {
181 dataObject = ObjectUtils.createNewObjectFromClass(dataObjectClass);
182 }
183
184 Object nestedObject = ObjectPropertyUtils.getPropertyValue(dataObject, localPrefix);
185 Class<?> nestedClass = null;
186 if (nestedObject == null) {
187 nestedClass = ObjectPropertyUtils.getPropertyType(dataObject, localPrefix);
188 } else {
189 nestedClass = nestedObject.getClass();
190 }
191
192 String fullPrefix = localPrefix;
193 if (StringUtils.isNotBlank(attributePrefix)) {
194 fullPrefix = attributePrefix + "." + localPrefix;
195 }
196
197 relationship = getDataObjectRelationship(nestedObject, nestedClass, localAttributeName, fullPrefix,
198 keysOnly, supportsLookup, supportsInquiry);
199
200 return relationship;
201 }
202
203
204 int maxSize = Integer.MAX_VALUE;
205
206
207 if (getPersistenceStructureService().isPersistable(dataObjectClass)) {
208 Map<String, DataObjectRelationship> rels = getPersistenceStructureService().getRelationshipMetadata(
209 dataObjectClass, attributeName, attributePrefix);
210 if (rels.size() > 0) {
211 for (DataObjectRelationship rel : rels.values()) {
212 if (rel.getParentToChildReferences().size() < maxSize) {
213 if (classHasSupportedFeatures(rel.getRelatedClass(), supportsLookup, supportsInquiry)) {
214 maxSize = rel.getParentToChildReferences().size();
215 relationship = rel;
216 }
217 }
218 }
219 }
220 } else {
221 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(dataObjectClass);
222 if (moduleService != null && moduleService.isExternalizable(dataObjectClass)) {
223 relationship = getRelationshipMetadata(dataObjectClass, attributeName, attributePrefix);
224 if ((relationship != null) && classHasSupportedFeatures(relationship.getRelatedClass(), supportsLookup,
225 supportsInquiry)) {
226 return relationship;
227 } else {
228 return null;
229 }
230 }
231 }
232
233 if (ddReference != null && ddReference.getPrimitiveAttributes().size() < maxSize) {
234 if (classHasSupportedFeatures(ddReference.getTargetClass(), supportsLookup, supportsInquiry)) {
235 relationship = populateRelationshipFromDictionaryReference(dataObjectClass, ddReference, null,
236 keysOnly);
237 }
238 }
239
240 return relationship;
241 }
242
243 protected boolean classHasSupportedFeatures(Class relationshipClass, boolean supportsLookup,
244 boolean supportsInquiry) {
245 boolean hasSupportedFeatures = true;
246 if (supportsLookup && !getViewDictionaryService().isLookupable(relationshipClass)) {
247 hasSupportedFeatures = false;
248 }
249 if (supportsInquiry && !getViewDictionaryService().isInquirable(relationshipClass)) {
250 hasSupportedFeatures = false;
251 }
252
253 return hasSupportedFeatures;
254 }
255
256
257
258
259
260
261
262
263
264 public RelationshipDefinition getDictionaryRelationship(Class<?> c, String attributeName) {
265 DataDictionaryEntry entryBase = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(
266 c.getName());
267 if (entryBase == null) {
268 return null;
269 }
270
271 RelationshipDefinition relationship = null;
272
273 List<RelationshipDefinition> ddRelationships = entryBase.getRelationships();
274
275 int minKeys = Integer.MAX_VALUE;
276 for (RelationshipDefinition def : ddRelationships) {
277
278 if (def.getPrimitiveAttributes().size() == 1) {
279 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) {
280 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals(
281 attributeName)) {
282 relationship = def;
283 minKeys = 1;
284 break;
285 }
286 }
287 } else if (def.getPrimitiveAttributes().size() < minKeys) {
288 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) {
289 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals(
290 attributeName)) {
291 relationship = def;
292 minKeys = def.getPrimitiveAttributes().size();
293 break;
294 }
295 }
296 }
297 }
298
299
300 if (relationship == null) {
301 for (RelationshipDefinition def : ddRelationships) {
302 if (def.hasIdentifier()) {
303 if (def.getIdentifier().getSourceName().equals(attributeName)) {
304 relationship = def;
305 }
306 }
307 }
308 }
309
310 return relationship;
311 }
312
313 protected DataObjectRelationship populateRelationshipFromDictionaryReference(Class<?> dataObjectClass,
314 RelationshipDefinition ddReference, String attributePrefix, boolean keysOnly) {
315 DataObjectRelationship relationship = new DataObjectRelationship(dataObjectClass,
316 ddReference.getObjectAttributeName(), ddReference.getTargetClass());
317
318 for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) {
319 if (StringUtils.isNotBlank(attributePrefix)) {
320 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
321 def.getTargetName());
322 } else {
323 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
324 }
325 }
326
327 if (!keysOnly) {
328 for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) {
329 if (StringUtils.isNotBlank(attributePrefix)) {
330 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(),
331 def.getTargetName());
332 if (def.isIdentifier()) {
333 relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName());
334 }
335 } else {
336 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
337 if (def.isIdentifier()) {
338 relationship.setUserVisibleIdentifierKey(def.getSourceName());
339 }
340 }
341 }
342 }
343
344 return relationship;
345 }
346
347 protected DataObjectRelationship getRelationshipMetadata(Class<?> dataObjectClass, String attributeName,
348 String attributePrefix) {
349
350 RelationshipDefinition relationshipDefinition = getDictionaryRelationship(dataObjectClass, attributeName);
351 if (relationshipDefinition == null) {
352 return null;
353 }
354
355 DataObjectRelationship dataObjectRelationship = new DataObjectRelationship(
356 relationshipDefinition.getSourceClass(), relationshipDefinition.getObjectAttributeName(),
357 relationshipDefinition.getTargetClass());
358
359 if (!StringUtils.isEmpty(attributePrefix)) {
360 attributePrefix += ".";
361 }
362
363 List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes();
364 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) {
365 dataObjectRelationship.getParentToChildReferences().put(
366 attributePrefix + primitiveAttributeDefinition.getSourceName(),
367 primitiveAttributeDefinition.getTargetName());
368 }
369
370 return dataObjectRelationship;
371 }
372
373
374
375
376 public String getTitleAttribute(Class<?> dataObjectClass) {
377 String titleAttribute = null;
378
379 DataObjectEntry entry = getDataObjectEntry(dataObjectClass);
380 if (entry != null) {
381 titleAttribute = entry.getTitleAttribute();
382 }
383
384 return titleAttribute;
385 }
386
387
388
389
390 @Override
391 public boolean areNotesSupported(Class<?> dataObjectClass) {
392 boolean hasNotesSupport = false;
393
394 DataObjectEntry entry = getDataObjectEntry(dataObjectClass);
395 if (entry != null) {
396 hasNotesSupport = entry.isBoNotesEnabled();
397 }
398
399 return hasNotesSupport;
400 }
401
402
403
404
405 public String getDataObjectIdentifierString(Object dataObject) {
406 String identifierString = "";
407
408 if (dataObject == null) {
409 identifierString = "Null";
410 return identifierString;
411 }
412
413 Class<?> dataObjectClass = dataObject.getClass();
414
415
416 if (PersistableBusinessObject.class.isAssignableFrom(dataObjectClass)) {
417 String objectId = ObjectPropertyUtils.getPropertyValue(dataObject, UifPropertyPaths.OBJECT_ID);
418 if (StringUtils.isBlank(objectId)) {
419 objectId = UUID.randomUUID().toString();
420 ObjectPropertyUtils.setPropertyValue(dataObject, UifPropertyPaths.OBJECT_ID, objectId);
421 }
422
423 identifierString = objectId;
424 } else {
425
426 Map<String, ?> primaryKeyFieldValues = getPrimaryKeyFieldValues(dataObject, true);
427 for (Map.Entry<String, ?> primaryKeyValue : primaryKeyFieldValues.entrySet()) {
428 if (primaryKeyValue.getValue() == null) {
429 identifierString += "Null";
430 } else {
431 identifierString += primaryKeyValue.getValue();
432 }
433 identifierString += ":";
434 }
435 identifierString = StringUtils.removeEnd(identifierString, ":");
436 }
437
438 return identifierString;
439 }
440
441
442
443
444
445
446
447 protected DataObjectEntry getDataObjectEntry(Class<?> dataObjectClass) {
448 if (dataObjectClass == null) {
449 throw new IllegalArgumentException("invalid (null) dataObjectClass");
450 }
451
452 DataObjectEntry entry = getDataDictionaryService().getDataDictionary().getDataObjectEntry(
453 dataObjectClass.getName());
454
455 return entry;
456 }
457
458 public List<DataObjectRelationship> getDataObjectRelationships(Class<?> dataObjectClass) {
459 if (dataObjectClass == null) {
460 return null;
461 }
462
463 Map<String, Class> referenceClasses = null;
464 if (PersistableBusinessObject.class.isAssignableFrom(dataObjectClass)
465 && getPersistenceStructureService().isPersistable(dataObjectClass)) {
466 referenceClasses = getPersistenceStructureService().listReferenceObjectFields(dataObjectClass);
467 }
468 DataDictionaryEntry ddEntry = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(
469 dataObjectClass.getName());
470 List<RelationshipDefinition> ddRelationships = (ddEntry == null ? new ArrayList<RelationshipDefinition>()
471 : ddEntry.getRelationships());
472 List<DataObjectRelationship> relationships = new ArrayList<DataObjectRelationship>();
473
474
475 if (referenceClasses != null) {
476 for (Map.Entry<String, Class> entry : referenceClasses.entrySet()) {
477 if (classHasSupportedFeatures(entry.getValue(), true, false)) {
478 Map<String, String> fkToPkRefs = getPersistenceStructureService().getForeignKeysForReference(dataObjectClass,
479 entry.getKey());
480 DataObjectRelationship rel = new DataObjectRelationship(dataObjectClass, entry.getKey(),
481 entry.getValue());
482 for (Map.Entry<String, String> ref : fkToPkRefs.entrySet()) {
483 rel.getParentToChildReferences().put(ref.getKey(), ref.getValue());
484 }
485 relationships.add(rel);
486 }
487 }
488 }
489
490 for (RelationshipDefinition rd : ddRelationships) {
491 if (classHasSupportedFeatures(rd.getTargetClass(), true, false)) {
492 DataObjectRelationship rel = new DataObjectRelationship(dataObjectClass, rd.getObjectAttributeName(),
493 rd.getTargetClass());
494 for (PrimitiveAttributeDefinition def : rd.getPrimitiveAttributes()) {
495 rel.getParentToChildReferences().put(def.getSourceName(), def.getTargetName());
496 }
497 relationships.add(rel);
498 }
499 }
500
501 return relationships;
502 }
503
504
505
506
507
508
509 protected BusinessObjectEntry getBusinessObjectEntry(Class businessObjectClass) {
510 validateBusinessObjectClass(businessObjectClass);
511
512 BusinessObjectEntry entry = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(
513 businessObjectClass.getName());
514 return entry;
515 }
516
517
518
519
520
521 protected void validateBusinessObjectClass(Class businessObjectClass) {
522 if (businessObjectClass == null) {
523 throw new IllegalArgumentException("invalid (null) dataObjectClass");
524 }
525 if (!BusinessObject.class.isAssignableFrom(businessObjectClass)) {
526 throw new IllegalArgumentException(
527 "class '" + businessObjectClass.getName() + "' is not a descendant of BusinessObject");
528 }
529 }
530
531
532
533
534
535
536 protected DataDictionaryService getDataDictionaryService() {
537 return this.dataDictionaryService;
538 }
539
540 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
541 this.dataDictionaryService = dataDictionaryService;
542 }
543
544
545
546
547
548
549 protected KualiModuleService getKualiModuleService() {
550 return this.kualiModuleService;
551 }
552
553 public void setKualiModuleService(KualiModuleService kualiModuleService) {
554 this.kualiModuleService = kualiModuleService;
555 }
556
557
558
559
560
561
562
563 protected PersistenceStructureService getPersistenceStructureService() {
564 return this.persistenceStructureService;
565 }
566
567 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
568 this.persistenceStructureService = persistenceStructureService;
569 }
570
571 protected ViewDictionaryService getViewDictionaryService() {
572 if (this.viewDictionaryService == null) {
573 this.viewDictionaryService = KRADServiceLocatorWeb.getViewDictionaryService();
574 }
575 return this.viewDictionaryService;
576 }
577
578 public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) {
579 this.viewDictionaryService = viewDictionaryService;
580 }
581
582 }