1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krms.impl.ui;
17
18 import org.apache.commons.collections.CollectionUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
21 import org.kuali.rice.core.api.data.DataType;
22 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
23 import org.kuali.rice.core.api.uif.RemotableAttributeField;
24 import org.kuali.rice.core.api.uif.RemotableTextInput;
25 import org.kuali.rice.core.api.util.tree.Node;
26 import org.kuali.rice.core.api.util.tree.Tree;
27 import org.kuali.rice.core.impl.cache.DistributedCacheManagerDecorator;
28 import org.kuali.rice.krad.data.PersistenceOption;
29 import org.kuali.rice.krad.maintenance.Maintainable;
30 import org.kuali.rice.krad.maintenance.MaintainableImpl;
31 import org.kuali.rice.krad.maintenance.MaintenanceDocument;
32 import org.kuali.rice.krad.uif.container.Container;
33 import org.kuali.rice.krad.uif.view.View;
34 import org.kuali.rice.krad.uif.view.ViewModel;
35 import org.kuali.rice.krad.util.KRADConstants;
36 import org.kuali.rice.krad.util.ObjectUtils;
37 import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
38 import org.kuali.rice.krms.api.KrmsConstants;
39 import org.kuali.rice.krms.api.repository.action.ActionDefinition;
40 import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
41 import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
42 import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
43 import org.kuali.rice.krms.api.repository.context.ContextDefinition;
44 import org.kuali.rice.krms.api.repository.function.FunctionDefinition;
45 import org.kuali.rice.krms.api.repository.operator.CustomOperator;
46 import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
47 import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
48 import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
49 import org.kuali.rice.krms.api.repository.term.TermDefinition;
50 import org.kuali.rice.krms.api.repository.term.TermResolverDefinition;
51 import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition;
52 import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
53 import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
54 import org.kuali.rice.krms.impl.repository.ActionAttributeBo;
55 import org.kuali.rice.krms.impl.repository.ActionBo;
56 import org.kuali.rice.krms.impl.repository.AgendaBo;
57 import org.kuali.rice.krms.impl.repository.AgendaItemBo;
58 import org.kuali.rice.krms.impl.repository.ContextBoService;
59 import org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionService;
60 import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
61 import org.kuali.rice.krms.impl.repository.PropositionBo;
62 import org.kuali.rice.krms.impl.repository.PropositionParameterBo;
63 import org.kuali.rice.krms.impl.repository.RepositoryBoIncrementer;
64 import org.kuali.rice.krms.impl.repository.RuleAttributeBo;
65 import org.kuali.rice.krms.impl.repository.RuleBo;
66 import org.kuali.rice.krms.impl.repository.TermBo;
67 import org.kuali.rice.krms.impl.repository.TermParameterBo;
68 import org.kuali.rice.krms.impl.util.KrmsImplConstants;
69 import org.kuali.rice.krms.impl.util.KrmsRetriever;
70 import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal;
71
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.Date;
75 import java.util.HashMap;
76 import java.util.List;
77 import java.util.Map;
78
79 import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching;
80
81
82
83
84
85
86 public class AgendaEditorMaintainable extends MaintainableImpl {
87
88 private static final long serialVersionUID = 1L;
89
90 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
91 AgendaEditorMaintainable.class);
92
93 public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document";
94 private static final RepositoryBoIncrementer termIdIncrementer = new RepositoryBoIncrementer(TermBo.TERM_SEQ_NAME);
95 private static final RepositoryBoIncrementer termParameterIdIncrementer = new RepositoryBoIncrementer(TermParameterBo.TERM_PARM_SEQ_NAME);
96 private static final RepositoryBoIncrementer agendaItemIncrementer = new RepositoryBoIncrementer(AgendaBo.AGENDA_SEQ_NAME);
97
98 private transient KrmsRetriever krmsRetriever = new KrmsRetriever();
99
100
101
102
103 private ContextBoService getContextBoService() {
104 return KrmsRepositoryServiceLocator.getContextBoService();
105 }
106
107 public List<RemotableAttributeField> retrieveAgendaCustomAttributes(View view, Object model, Container container) {
108 AgendaEditor agendaEditor = getAgendaEditor(model);
109 return krmsRetriever.retrieveAgendaCustomAttributes(agendaEditor);
110 }
111
112
113
114
115
116 public List<RemotableAttributeField> retrieveTermParameters(View view, Object model, Container container) {
117
118 List<RemotableAttributeField> results = new ArrayList<RemotableAttributeField>();
119
120 AgendaEditor agendaEditor = getAgendaEditor(model);
121
122
123 RuleBo rule = agendaEditor.getAgendaItemLine().getRule();
124 if (null != rule) {
125
126
127 Tree<RuleTreeNode, String> propositionTree = rule.getPropositionTree();
128 Node<RuleTreeNode, String> editedPropositionNode = findEditedProposition(propositionTree.getRootElement());
129
130 if (editedPropositionNode != null) {
131 PropositionBo propositionBo = editedPropositionNode.getData().getProposition();
132 if (StringUtils.isEmpty(propositionBo.getCompoundOpCode()) && CollectionUtils.size(
133 propositionBo.getParameters()) > 0) {
134
135 PropositionParameterBo param = propositionBo.getParameters().get(0);
136 if (StringUtils.isNotBlank(param.getValue()) &&
137 param.getValue().startsWith(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
138 String termSpecId = param.getValue().substring(
139 KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length());
140 TermResolverDefinition simplestResolver = getSimplestTermResolver(termSpecId,
141 rule.getNamespace());
142
143
144 if (simplestResolver != null) {
145 List<String> parameterNames = new ArrayList<String>(simplestResolver.getParameterNames());
146 Collections.sort(parameterNames);
147
148 for (String parameterName : parameterNames) {
149
150 RemotableTextInput.Builder controlBuilder = RemotableTextInput.Builder.create();
151 controlBuilder.setSize(64);
152
153 RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create(
154 parameterName);
155
156 builder.setRequired(true);
157 builder.setDataType(DataType.STRING);
158 builder.setControl(controlBuilder);
159 builder.setLongLabel(parameterName);
160 builder.setShortLabel(parameterName);
161 builder.setMinLength(Integer.valueOf(1));
162 builder.setMaxLength(Integer.valueOf(64));
163
164 results.add(builder.build());
165 }
166 }
167 }
168 }
169 }
170 }
171 return results;
172 }
173
174
175
176
177
178
179
180
181
182 static TermResolverDefinition getSimplestTermResolver(String termSpecId,
183 String namespace) {
184
185 List<TermResolverDefinition> resolvers =
186 KrmsRepositoryServiceLocator.getTermBoService().findTermResolversByOutputId(termSpecId, namespace);
187
188 TermResolverDefinition simplestResolver = null;
189
190 for (TermResolverDefinition resolver : resolvers) {
191 if (simplestResolver == null || simplestResolver.getParameterNames().size() < resolver.getParameterNames()
192 .size()) {
193 simplestResolver = resolver;
194 }
195 }
196
197 return simplestResolver;
198 }
199
200
201
202
203
204
205
206 private Node<RuleTreeNode, String> findEditedProposition(Node<RuleTreeNode, String> node) {
207 Node<RuleTreeNode, String> result = null;
208 if (node.getData() != null && node.getData().getProposition() != null && node.getData().getProposition()
209 .getEditMode()) {
210 result = node;
211 } else {
212 for (Node<RuleTreeNode, String> child : node.getChildren()) {
213 result = findEditedProposition(child);
214 if (result != null) {
215 break;
216 }
217 }
218 }
219 return result;
220 }
221
222
223
224
225
226
227
228 private AgendaEditor getAgendaEditor(Object model) {
229 MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
230 return (AgendaEditor) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject();
231 }
232
233 public List<RemotableAttributeField> retrieveRuleActionCustomAttributes(View view, Object model,
234 Container container) {
235 AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model);
236 return krmsRetriever.retrieveRuleActionCustomAttributes(agendaEditor);
237 }
238
239
240
241
242 public List<RemotableAttributeField> retrieveRuleCustomAttributes(View view, Object model, Container container) {
243 AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model);
244 return krmsRetriever.retrieveRuleCustomAttributes(agendaEditor);
245 }
246
247 @Override
248 public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
249 Object dataObject = null;
250
251 try {
252
253 AgendaEditor agendaEditor = new AgendaEditor();
254 AgendaBo agenda = findSingleMatching(getDataObjectService(),
255 ((AgendaEditor) getDataObject()).getAgenda().getClass(), dataObjectKeys);
256
257
258 forceLoadLazyRelations(agenda);
259
260 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) {
261 String dateTimeStamp = (new Date()).getTime() + "";
262 String newAgendaName = AgendaItemBo.COPY_OF_TEXT + agenda.getName() + " " + dateTimeStamp;
263
264 AgendaBo copiedAgenda = agenda.copyAgenda(newAgendaName, dateTimeStamp);
265
266 document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
267 document.setFieldsClearedOnCopy(true);
268 agendaEditor.setAgenda(copiedAgenda);
269 } else {
270
271
272 agendaEditor.setAgenda(agenda);
273 }
274 agendaEditor.setCustomAttributesMap(agenda.getAttributes());
275
276
277 agendaEditor.setNamespace(agenda.getContext().getNamespace());
278 agendaEditor.setContextName(agenda.getContext().getName());
279
280 dataObject = agendaEditor;
281 } catch (ClassNotPersistenceCapableException ex) {
282 if (!document.getOldMaintainableObject().isExternalBusinessObject()) {
283 throw new RuntimeException("Data Object Class: "
284 + getDataObjectClass()
285 + " is not persistable and is not externalizable - configuration error");
286 }
287
288 }
289
290 return dataObject;
291 }
292
293 private void forceLoadLazyRelations(AgendaBo agenda) {
294 for (AgendaItemBo item : agenda.getItems()) {
295 for (ActionBo action : item.getRule().getActions()) {
296 if (CollectionUtils.isEmpty(action.getAttributeBos())) { continue; }
297
298 for (ActionAttributeBo actionAttribute : action.getAttributeBos()) {
299 actionAttribute.getAttributeDefinition();
300 }
301 }
302
303 Tree propTree = item.getRule().refreshPropositionTree(true);
304 walkPropositionTree(item.getRule().getProposition());
305
306 for (RuleAttributeBo ruleAttribute : item.getRule().getAttributeBos()) {
307 ruleAttribute.getAttributeDefinition();
308 }
309 }
310 }
311
312 private void walkPropositionTree(PropositionBo prop) {
313 if (prop == null) { return; }
314
315 if (prop.getParameters() != null) for (PropositionParameterBo param : prop.getParameters()) {
316 param.getPropId();
317 }
318
319 if (prop.getCompoundComponents() != null) for (PropositionBo childProp : prop.getCompoundComponents()) {
320 walkPropositionTree(childProp);
321 }
322 }
323
324
325
326
327 @Override
328 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
329 super.processAfterNew(document, requestParameters);
330 document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
331 }
332
333 @Override
334 public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
335 super.processAfterEdit(document, requestParameters);
336 document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document");
337 }
338
339 @Override
340 public void processAfterCopy(MaintenanceDocument document,
341 Map<String, String[]> parameters) {
342 super.processAfterCopy(document, parameters);
343 AgendaBo agendaBo = ((AgendaEditor) document.getDocumentDataObject()).getAgenda();
344 agendaBo.setVersionNumber(null);
345
346 for (AgendaItemBo agendaItem : agendaBo.getItems()) {
347 agendaItem.setVersionNumber(null);
348 }
349 }
350 @Override
351 public void prepareForSave() {
352
353 AgendaEditor agendaEditor = (AgendaEditor) getDataObject();
354 agendaEditor.getAgenda().setAttributes(agendaEditor.getCustomAttributesMap());
355 }
356
357 @Override
358 public void saveDataObject() {
359 AgendaBo agendaBo = ((AgendaEditor) getDataObject()).getAgenda();
360
361 AgendaItemBo firstItem = null;
362
363
364 for (AgendaItemBo agendaItem : agendaBo.getItems()) {
365 if (agendaBo.getFirstItemId().equals(agendaItem.getId())) {
366 firstItem = agendaItem;
367 }
368 }
369
370
371
372 List<AgendaItemBo> agendaItems = agendaBo.getItems();
373 List<AgendaItemBo> updatedItems = new ArrayList<AgendaItemBo>();
374 if (agendaBo.getId() == null || getDataObjectService().find(AgendaBo.class,agendaBo.getId()) == null) {
375 agendaBo.setItems(updatedItems);
376 agendaBo.setFirstItem(null);
377 agendaBo = getDataObjectService().save(agendaBo);
378 getDataObjectService().flush(AgendaBo.class);
379 String agendaBoId = agendaBo.getId();
380 agendaBo = getDataObjectService().find(AgendaBo.class,agendaBoId);
381 agendaBo.setItems(agendaItems);
382 agendaBo.setFirstItem(firstItem);
383 }
384
385
386
387 for (AgendaItemBo agendaItem : agendaBo.getItems()) {
388 PropositionBo propositionBo = agendaItem.getRule().getProposition();
389 if (propositionBo != null) {
390 saveNewParameterizedTerms(propositionBo);
391 processCustomOperators(propositionBo);
392 }
393 }
394
395 if (agendaBo != null) {
396 flushCacheBeforeSave();
397 getDataObjectService().flush(AgendaBo.class);
398
399
400 agendaBo.setFirstItem(firstItem);
401 getDataObjectService().save(agendaBo);
402
403
404 for (String deletedPropId : ((AgendaEditor) getDataObject()).getDeletedPropositionIds()) {
405 PropositionBo toDelete = getDataObjectService().find(PropositionBo.class, deletedPropId);
406
407 deletePropositionFromTree(toDelete);
408 }
409 } else {
410 throw new RuntimeException("Cannot save null " + AgendaBo.class.getName() + " with business object service");
411 }
412 }
413
414
415
416
417
418
419
420
421 private void deletePropositionFromTree(PropositionBo toDelete) {
422 if (toDelete != null) {
423
424 if (!CollectionUtils.isEmpty(toDelete.getCompoundComponents())) {
425 toDelete.getCompoundComponents().clear();
426 }
427
428
429 getDataObjectService().save(toDelete);
430 getDataObjectService().delete(toDelete);
431 }
432 }
433
434 private void addItemsToListForDeletion(List<AgendaItemBo> deletionOrder, AgendaItemBo agendaItemBo){
435 if (!deletionOrder.contains(agendaItemBo) && ObjectUtils.isNotNull(agendaItemBo)) {
436 deletionOrder.add(agendaItemBo);
437 }
438 if (ObjectUtils.isNotNull(agendaItemBo)) {
439 if (StringUtils.isNotBlank(agendaItemBo.getWhenTrueId()) &&
440 !deletionOrder.contains(agendaItemBo.getWhenTrue())) {
441 deletionOrder.add(agendaItemBo.getWhenTrue());
442 addItemsToListForDeletion (deletionOrder, agendaItemBo.getWhenTrue());
443 }
444 if (StringUtils.isNotBlank(agendaItemBo.getWhenFalseId()) &&
445 !deletionOrder.contains(agendaItemBo.getWhenFalse())) {
446 deletionOrder.add(agendaItemBo.getWhenFalse());
447 addItemsToListForDeletion (deletionOrder, agendaItemBo.getWhenFalse());
448 }
449 if (StringUtils.isNotBlank(agendaItemBo.getAlwaysId()) &&
450 !deletionOrder.contains(agendaItemBo.getAlways())) {
451 deletionOrder.add(agendaItemBo.getAlways());
452 addItemsToListForDeletion (deletionOrder,agendaItemBo.getAlways());
453 }
454 }
455 }
456
457 private void flushCacheBeforeSave(){
458
459 DistributedCacheManagerDecorator distributedCacheManagerDecorator =
460 GlobalResourceLoader.getService(KrmsConstants.KRMS_DISTRIBUTED_CACHE);
461
462 distributedCacheManagerDecorator.getCache(ActionDefinition.Cache.NAME).clear();
463 distributedCacheManagerDecorator.getCache(AgendaItemDefinition.Cache.NAME).clear();
464 distributedCacheManagerDecorator.getCache(AgendaTreeDefinition.Cache.NAME).clear();
465 distributedCacheManagerDecorator.getCache(AgendaDefinition.Cache.NAME).clear();
466 distributedCacheManagerDecorator.getCache(ContextDefinition.Cache.NAME).clear();
467 distributedCacheManagerDecorator.getCache(KrmsAttributeDefinition.Cache.NAME).clear();
468 distributedCacheManagerDecorator.getCache(KrmsTypeDefinition.Cache.NAME).clear();
469 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
470 distributedCacheManagerDecorator.getCache(PropositionDefinition.Cache.NAME).clear();
471 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
472 distributedCacheManagerDecorator.getCache(TermDefinition.Cache.NAME).clear();
473 distributedCacheManagerDecorator.getCache(TermResolverDefinition.Cache.NAME).clear();
474 distributedCacheManagerDecorator.getCache(TermSpecificationDefinition.Cache.NAME).clear();
475 }
476
477
478
479
480
481
482 private void saveNewParameterizedTerms(PropositionBo propositionBo) {
483 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
484
485 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(0).getValue().startsWith(
486 KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
487 String termId = propositionBo.getParameters().get(0).getValue();
488 String termSpecId = termId.substring(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length());
489
490 TermBo newTerm = new TermBo();
491 newTerm.setDescription(propositionBo.getNewTermDescription());
492 newTerm.setSpecificationId(termSpecId);
493 newTerm.setId(termIdIncrementer.getNewId());
494
495 List<TermParameterBo> params = new ArrayList<TermParameterBo>();
496 for (Map.Entry<String, String> entry : propositionBo.getTermParameters().entrySet()) {
497 TermParameterBo param = new TermParameterBo();
498 param.setTerm(newTerm);
499 param.setName(entry.getKey());
500 param.setValue(entry.getValue());
501 param.setId(termParameterIdIncrementer.getNewId());
502
503 params.add(param);
504 }
505
506 newTerm.setParameters(params);
507
508 getLegacyDataAdapter().linkAndSave(newTerm);
509 propositionBo.getParameters().get(0).setValue(newTerm.getId());
510 }
511 } else {
512
513 for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
514 saveNewParameterizedTerms(childProp);
515 }
516 }
517 }
518
519
520
521
522
523
524 private void processCustomOperators(PropositionBo propositionBo) {
525 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
526
527 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(2).getValue().startsWith(
528 KrmsImplConstants.CUSTOM_OPERATOR_PREFIX)) {
529 PropositionParameterBo operatorParam = propositionBo.getParameters().get(2);
530
531 CustomOperator customOperator =
532 KrmsServiceLocatorInternal.getCustomOperatorUiTranslator().getCustomOperator(operatorParam.getValue());
533
534 FunctionDefinition operatorFunctionDefinition = customOperator.getOperatorFunctionDefinition();
535
536 operatorParam.setParameterType(PropositionParameterType.FUNCTION.getCode());
537 operatorParam.setValue(operatorFunctionDefinition.getId());
538 }
539 } else {
540
541 for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
542 processCustomOperators(childProp);
543 }
544 }
545 }
546
547
548
549
550
551
552
553
554 private Map<String, KrmsAttributeDefinition> buildAttributeDefinitionMap(String agendaTypeId) {
555 KrmsAttributeDefinitionService attributeDefinitionService =
556 KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
557
558
559 Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
560
561 List<KrmsAttributeDefinition> attributeDefinitions = attributeDefinitionService.findAttributeDefinitionsByType(
562 agendaTypeId);
563
564 for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
565 attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
566 }
567 return attributeDefinitionMap;
568 }
569
570 @Override
571 public boolean isOldDataObjectInDocument() {
572 boolean isOldDataObjectInExistence = true;
573
574 if (getDataObject() == null) {
575 isOldDataObjectInExistence = false;
576 } else {
577
578 Map<String, ?> keyFieldValues = getLegacyDataAdapter().getPrimaryKeyFieldValues(
579 ((AgendaEditor) getDataObject()).getAgenda());
580 for (Object keyValue : keyFieldValues.values()) {
581 if (keyValue == null) {
582 isOldDataObjectInExistence = false;
583 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
584 isOldDataObjectInExistence = false;
585 }
586
587 if (!isOldDataObjectInExistence) {
588 break;
589 }
590 }
591 }
592
593 return isOldDataObjectInExistence;
594 }
595
596
597 @Override
598 public Class getDataObjectClass() {
599 return AgendaBo.class;
600 }
601
602 @Override
603 public boolean isLockable() {
604 return true;
605 }
606
607 @Override
608 public void processBeforeAddLine(ViewModel model, Object addLine, String collectionId, String collectionPath) {
609 AgendaEditor agendaEditor = getAgendaEditor(model);
610 if (addLine instanceof ActionBo) {
611 ((ActionBo) addLine).setNamespace(agendaEditor.getAgendaItemLine().getRule().getNamespace());
612 }
613
614 super.processBeforeAddLine(model, addLine, collectionId, collectionPath);
615 }
616 }