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