1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.workflow.service.impl;
17
18 import org.joda.time.DateTime;
19 import org.kuali.rice.core.api.util.type.KualiDecimal;
20 import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
21 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeDateTime;
22 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeDecimal;
23 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory;
24 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeInteger;
25 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeString;
26 import org.kuali.rice.kew.api.KewApiConstants;
27 import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
28 import org.kuali.rice.kns.service.KNSServiceLocator;
29 import org.kuali.rice.krad.bo.BusinessObject;
30 import org.kuali.rice.krad.bo.PersistableBusinessObject;
31 import org.kuali.rice.krad.datadictionary.DocumentCollectionPath;
32 import org.kuali.rice.krad.datadictionary.DocumentValuePathGroup;
33 import org.kuali.rice.krad.datadictionary.RoutingAttribute;
34 import org.kuali.rice.krad.datadictionary.RoutingTypeDefinition;
35 import org.kuali.rice.krad.datadictionary.SearchingTypeDefinition;
36 import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
37 import org.kuali.rice.krad.document.Document;
38 import org.kuali.rice.krad.service.PersistenceStructureService;
39 import org.kuali.rice.krad.util.DataTypeUtil;
40 import org.kuali.rice.krad.util.ObjectUtils;
41 import org.kuali.rice.krad.workflow.attribute.DataDictionarySearchableAttribute;
42 import org.kuali.rice.krad.workflow.service.WorkflowAttributePropertyResolutionService;
43
44 import java.math.BigDecimal;
45 import java.math.BigInteger;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 import java.util.Stack;
54
55
56
57
58 public class WorkflowAttributePropertyResolutionServiceImpl implements WorkflowAttributePropertyResolutionService {
59
60 private PersistenceStructureService persistenceStructureService;
61 private BusinessObjectMetaDataService businessObjectMetaDataService;
62
63
64
65
66 public List<Map<String, String>> resolveRoutingTypeQualifiers(Document document, RoutingTypeDefinition routingTypeDefinition) {
67 List<Map<String, String>> qualifiers = new ArrayList<Map<String, String>>();
68
69 if (routingTypeDefinition != null) {
70 document.populateDocumentForRouting();
71 RoutingAttributeTracker routingAttributeTracker = new RoutingAttributeTracker(routingTypeDefinition.getRoutingAttributes());
72 for (DocumentValuePathGroup documentValuePathGroup : routingTypeDefinition.getDocumentValuePathGroups()) {
73 qualifiers.addAll(resolveDocumentValuePath(document, documentValuePathGroup, routingAttributeTracker));
74 routingAttributeTracker.reset();
75 }
76 }
77 return qualifiers;
78 }
79
80
81
82
83
84
85
86 protected List<Map<String, String>> resolveDocumentValuePath(BusinessObject businessObject, DocumentValuePathGroup group, RoutingAttributeTracker routingAttributeTracker) {
87 List<Map<String, String>> qualifiers;
88 Map<String, String> qualifier = new HashMap<String, String>();
89 if (group.getDocumentValues() == null && group.getDocumentCollectionPath() == null) {
90 throw new IllegalStateException("A document value path group must have the documentValues property set, the documentCollectionPath property set, or both.");
91 }
92 if (group.getDocumentValues() != null) {
93 addPathValuesToQualifier(businessObject, group.getDocumentValues(), routingAttributeTracker, qualifier);
94 }
95 if (group.getDocumentCollectionPath() != null) {
96 qualifiers = resolveDocumentCollectionPath(businessObject, group.getDocumentCollectionPath(), routingAttributeTracker);
97 qualifiers = cleanCollectionQualifiers(qualifiers);
98 for (Map<String, String> collectionElementQualifier : qualifiers) {
99 copyQualifications(qualifier, collectionElementQualifier);
100 }
101 } else {
102 qualifiers = new ArrayList<Map<String, String>>();
103 qualifiers.add(qualifier);
104 }
105 return qualifiers;
106 }
107
108
109
110
111
112
113
114 protected List<Map<String, String>> resolveDocumentCollectionPath(BusinessObject businessObject, DocumentCollectionPath collectionPath, RoutingAttributeTracker routingAttributeTracker) {
115 List<Map<String, String>> qualifiers = new ArrayList<Map<String, String>>();
116 final Collection collectionByPath = getCollectionByPath(businessObject, collectionPath.getCollectionPath());
117 if (!ObjectUtils.isNull(collectionByPath)) {
118 if (collectionPath.getNestedCollection() != null) {
119
120 for (Object collectionElement : collectionByPath) {
121
122 if (collectionElement instanceof BusinessObject) {
123 List<Map<String, String>> childQualifiers = resolveDocumentCollectionPath((BusinessObject)collectionElement, collectionPath.getNestedCollection(), routingAttributeTracker);
124 for (Map<String, String> childQualifier : childQualifiers) {
125 Map<String, String> qualifier = new HashMap<String, String>();
126 routingAttributeTracker.checkPoint();
127
128 addPathValuesToQualifier(collectionElement, collectionPath.getDocumentValues(), routingAttributeTracker, qualifier);
129
130 copyQualifications(childQualifier, qualifier);
131 qualifiers.add(qualifier);
132 routingAttributeTracker.backUpToCheckPoint();
133 }
134 }
135 }
136 } else {
137
138 for (Object collectionElement : collectionByPath) {
139 Map<String, String> qualifier = new HashMap<String, String>();
140 routingAttributeTracker.checkPoint();
141 addPathValuesToQualifier(collectionElement, collectionPath.getDocumentValues(), routingAttributeTracker, qualifier);
142 qualifiers.add(qualifier);
143 routingAttributeTracker.backUpToCheckPoint();
144 }
145 }
146 }
147 return qualifiers;
148 }
149
150
151
152
153
154
155
156 protected Collection getCollectionByPath(BusinessObject businessObject, String collectionPath) {
157 return (Collection)getPropertyByPath(businessObject, collectionPath.trim());
158 }
159
160
161
162
163
164
165
166
167 protected void addPathValuesToQualifier(Object businessObject, List<String> paths, RoutingAttributeTracker routingAttributes, Map<String, String> qualifier) {
168 if (ObjectUtils.isNotNull(paths)) {
169 for (String path : paths) {
170
171 final Object value = getPropertyByPath(businessObject, path.trim());
172 if (value != null) {
173 qualifier.put(routingAttributes.getCurrentRoutingAttribute().getQualificationAttributeName(), value.toString());
174 }
175 routingAttributes.moveToNext();
176 }
177 }
178 }
179
180
181
182
183
184
185 protected void copyQualifications(Map<String, String> source, Map<String, String> target) {
186 for (String key : source.keySet()) {
187 target.put(key, source.get(key));
188 }
189 }
190
191
192
193
194
195 public List<DocumentAttribute> resolveSearchableAttributeValues(Document document, WorkflowAttributes workflowAttributes) {
196 List<DocumentAttribute> valuesToIndex = new ArrayList<DocumentAttribute>();
197 if (workflowAttributes != null && workflowAttributes.getSearchingTypeDefinitions() != null) {
198 for (SearchingTypeDefinition definition : workflowAttributes.getSearchingTypeDefinitions()) {
199 valuesToIndex.addAll(aardvarkValuesForSearchingTypeDefinition(document, definition));
200 }
201 }
202 return valuesToIndex;
203 }
204
205
206
207
208
209
210
211 protected List<DocumentAttribute> aardvarkValuesForSearchingTypeDefinition(Document document, SearchingTypeDefinition searchingTypeDefinition) {
212 List<DocumentAttribute> searchAttributes = new ArrayList<DocumentAttribute>();
213
214 final List<Object> searchValues = aardvarkSearchValuesForPaths(document, searchingTypeDefinition.getDocumentValues());
215 for (Object value : searchValues) {
216 try {
217 final DocumentAttribute searchableAttributeValue = buildSearchableAttribute(((Class<? extends BusinessObject>)Class.forName(searchingTypeDefinition.getSearchingAttribute().getBusinessObjectClassName())), searchingTypeDefinition.getSearchingAttribute().getAttributeName(), value);
218 if (searchableAttributeValue != null) {
219 searchAttributes.add(searchableAttributeValue);
220 }
221 }
222 catch (ClassNotFoundException cnfe) {
223 throw new RuntimeException("Could not find instance of class "+searchingTypeDefinition.getSearchingAttribute().getBusinessObjectClassName(), cnfe);
224 }
225 }
226 return searchAttributes;
227 }
228
229
230
231
232
233
234
235 protected List<Object> aardvarkSearchValuesForPaths(Document document, List<String> paths) {
236 List<Object> searchValues = new ArrayList<Object>();
237 for (String path : paths) {
238 flatAdd(searchValues, getPropertyByPath(document, path.trim()));
239 }
240 return searchValues;
241 }
242
243
244
245
246
247
248 protected List<Map<String, String>> cleanCollectionQualifiers(List<Map<String, String>> qualifiers) {
249 List<Map<String, String>> cleanedQualifiers = new ArrayList<Map<String, String>>();
250 for (Map<String, String> qualifier : qualifiers) {
251 if (qualifier.size() > 0) {
252 cleanedQualifiers.add(qualifier);
253 }
254 }
255 return cleanedQualifiers;
256 }
257
258 public String determineFieldDataType(Class<? extends BusinessObject> businessObjectClass, String attributeName) {
259 return DataTypeUtil.determineFieldDataType(businessObjectClass, attributeName);
260 }
261
262
263
264
265
266
267
268 public DocumentAttribute buildSearchableAttribute(Class<? extends BusinessObject> businessObjectClass, String attributeKey, Object value) {
269 if (value == null) return null;
270 final String fieldDataType = determineFieldDataType(businessObjectClass, attributeKey);
271 if (fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_STRING)) return buildSearchableStringAttribute(attributeKey, value);
272 if (fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_FLOAT) && DataTypeUtil.isDecimaltastic(value.getClass())) return buildSearchableRealAttribute(attributeKey, value);
273 if (fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_DATE) && DataTypeUtil.isDateLike(value.getClass())) return buildSearchableDateTimeAttribute(attributeKey, value);
274 if (fieldDataType.equals(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_LONG) && DataTypeUtil.isIntsy(value.getClass())) return buildSearchableFixnumAttribute(attributeKey, value);
275 if (fieldDataType.equals(DataDictionarySearchableAttribute.DATA_TYPE_BOOLEAN) && DataTypeUtil.isBooleanable(value.getClass())) return buildSearchableYesNoAttribute(attributeKey, value);
276 return buildSearchableStringAttribute(attributeKey, value);
277 }
278
279
280
281
282
283
284
285 protected DocumentAttributeDateTime buildSearchableDateTimeAttribute(String attributeKey, Object value) {
286 return DocumentAttributeFactory.createDateTimeAttribute(attributeKey, new DateTime(value));
287 }
288
289
290
291
292
293
294
295 protected DocumentAttributeDecimal buildSearchableRealAttribute(String attributeKey, Object value) {
296 BigDecimal decimalValue = null;
297 if (value instanceof BigDecimal) {
298 decimalValue = (BigDecimal)value;
299 } else if (value instanceof KualiDecimal) {
300 decimalValue = ((KualiDecimal)value).bigDecimalValue();
301 } else {
302 decimalValue = new BigDecimal(((Number)value).doubleValue());
303 }
304 return DocumentAttributeFactory.createDecimalAttribute(attributeKey, decimalValue);
305 }
306
307
308
309
310
311
312
313 protected DocumentAttributeInteger buildSearchableFixnumAttribute(String attributeKey, Object value) {
314 BigInteger integerValue = null;
315 if (value instanceof BigInteger) {
316 integerValue = (BigInteger)value;
317 } else {
318 integerValue = BigInteger.valueOf(((Number)value).longValue());
319 }
320 return DocumentAttributeFactory.createIntegerAttribute(attributeKey, integerValue);
321 }
322
323
324
325
326
327
328
329 protected DocumentAttributeString buildSearchableStringAttribute(String attributeKey, Object value) {
330 return DocumentAttributeFactory.createStringAttribute(attributeKey, value.toString());
331 }
332
333
334
335
336
337
338
339 protected DocumentAttributeString buildSearchableYesNoAttribute(String attributeKey, Object value) {
340 final String boolValueAsString = booleanValueAsString((Boolean)value);
341 return DocumentAttributeFactory.createStringAttribute(attributeKey, boolValueAsString);
342 }
343
344
345
346
347
348
349 private String booleanValueAsString(Boolean booleanValue) {
350 if (booleanValue == null) return "";
351 if (booleanValue.booleanValue()) return "Y";
352 return "N";
353 }
354
355 public Object getPropertyByPath(Object object, String path) {
356 if (object instanceof Collection) return getPropertyOfCollectionByPath((Collection)object, path);
357
358 final String[] splitPath = headAndTailPath(path);
359 final String head = splitPath[0];
360 final String tail = splitPath[1];
361
362 if (object instanceof PersistableBusinessObject && tail != null) {
363 if (getBusinessObjectMetaDataService().getBusinessObjectRelationship((BusinessObject) object, head) != null) {
364 ((PersistableBusinessObject)object).refreshReferenceObject(head);
365
366 }
367 }
368 final Object headValue = ObjectUtils.getPropertyValue(object, head);
369 if (!ObjectUtils.isNull(headValue)) {
370 if (tail == null) {
371 return headValue;
372 } else {
373
374 if (headValue instanceof Collection) {
375
376 Collection values = makeNewCollectionOfSameType((Collection)headValue);
377 for (Object currentElement : (Collection)headValue) {
378 flatAdd(values, getPropertyByPath(currentElement, tail));
379 }
380 return values;
381 } else {
382 return getPropertyByPath(headValue, tail);
383 }
384 }
385 }
386 return null;
387 }
388
389
390
391
392
393
394
395 public Collection getPropertyOfCollectionByPath(Collection collection, String path) {
396 Collection values = makeNewCollectionOfSameType(collection);
397 for (Object o : collection) {
398 flatAdd(values, getPropertyByPath(o, path));
399 }
400 return values;
401 }
402
403
404
405
406
407
408 public Collection makeNewCollectionOfSameType(Collection collection) {
409 if (collection instanceof List) return new ArrayList();
410 if (collection instanceof Set) return new HashSet();
411 try {
412 return collection.getClass().newInstance();
413 }
414 catch (InstantiationException ie) {
415 throw new RuntimeException("Couldn't instantiate class of collection we'd already instantiated??", ie);
416 }
417 catch (IllegalAccessException iae) {
418 throw new RuntimeException("Illegal Access on class of collection we'd already accessed??", iae);
419 }
420 }
421
422
423
424
425
426
427 protected String[] headAndTailPath(String path) {
428 final int firstDot = path.indexOf('.');
429 if (firstDot < 0) {
430 return new String[] { path, null };
431 }
432 return new String[] { path.substring(0, firstDot), path.substring(firstDot + 1) };
433 }
434
435
436
437
438
439
440 protected void flatAdd(Collection c, Object o) {
441 if (o instanceof Collection) {
442 c.addAll((Collection) o);
443 } else {
444 c.add(o);
445 }
446 }
447
448
449
450
451
452 public PersistenceStructureService getPersistenceStructureService() {
453 return persistenceStructureService;
454 }
455
456
457
458
459
460 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
461 this.persistenceStructureService = persistenceStructureService;
462 }
463
464
465
466
467 class RoutingAttributeTracker {
468
469 private List<RoutingAttribute> routingAttributes;
470 private int currentRoutingAttributeIndex;
471 private Stack<Integer> checkPoints;
472
473
474
475
476
477 public RoutingAttributeTracker(List<RoutingAttribute> routingAttributes) {
478 this.routingAttributes = routingAttributes;
479 checkPoints = new Stack<Integer>();
480 }
481
482
483
484
485 public RoutingAttribute getCurrentRoutingAttribute() {
486 return routingAttributes.get(currentRoutingAttributeIndex);
487 }
488
489
490
491
492 public void moveToNext() {
493 currentRoutingAttributeIndex += 1;
494 }
495
496
497
498
499 public void checkPoint() {
500 checkPoints.push(new Integer(currentRoutingAttributeIndex));
501 }
502
503
504
505
506 public void backUpToCheckPoint() {
507 currentRoutingAttributeIndex = checkPoints.pop().intValue();
508 }
509
510
511
512
513
514 public void reset() {
515 currentRoutingAttributeIndex = 0;
516 checkPoints.clear();
517 }
518 }
519
520 protected BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
521 if ( businessObjectMetaDataService == null ) {
522 businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService();
523 }
524 return businessObjectMetaDataService;
525 }
526 }