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 List<AgendaItemBo> deletedItems = new ArrayList<AgendaItemBo>();
375 AgendaBo existing = null;
376
377 if (agendaBo.getId() != null) {
378 existing = getDataObjectService().find(AgendaBo.class, agendaBo.getId());
379 }
380
381 if (existing == null) {
382 agendaBo.setItems(updatedItems);
383 agendaBo.setFirstItem(null);
384 agendaBo = getDataObjectService().save(agendaBo);
385 getDataObjectService().flush(AgendaBo.class);
386 String agendaBoId = agendaBo.getId();
387 agendaBo = getDataObjectService().find(AgendaBo.class,agendaBoId);
388 agendaBo.setItems(agendaItems);
389 agendaBo.setFirstItem(firstItem);
390 } else {
391
392 for (AgendaItemBo existingAgendaItem : existing.getItems()) {
393 boolean deletedAgendaItem = true;
394 for (AgendaItemBo agendaItem : agendaBo.getItems()) {
395 if (agendaItem.getId().equalsIgnoreCase(existingAgendaItem.getId())) {
396 deletedAgendaItem = false;
397 break;
398 }
399 }
400 if (deletedAgendaItem) {
401 deletedItems.add(existingAgendaItem);
402 }
403 }
404 }
405
406
407 for (AgendaItemBo agendaItem : agendaBo.getItems()) {
408 PropositionBo propositionBo = agendaItem.getRule().getProposition();
409 if (propositionBo != null) {
410 saveNewParameterizedTerms(propositionBo);
411 processCustomOperators(propositionBo);
412 }
413 }
414
415 if (agendaBo != null) {
416 flushCacheBeforeSave();
417 getDataObjectService().flush(AgendaBo.class);
418
419
420 agendaBo.setFirstItem(firstItem);
421 getDataObjectService().save(agendaBo);
422
423
424 for (String deletedPropId : ((AgendaEditor) getDataObject()).getDeletedPropositionIds()) {
425 PropositionBo toDelete = getDataObjectService().find(PropositionBo.class, deletedPropId);
426
427 getDataObjectService().delete(toDelete);
428 }
429
430 for (AgendaItemBo deletedItem : deletedItems) {
431 if (deletedItem.getRule() != null) {
432 getDataObjectService().delete(deletedItem.getRule());
433 }
434 }
435 } else {
436 throw new RuntimeException("Cannot save null " + AgendaBo.class.getName() + " with business object service");
437 }
438 }
439
440 private void flushCacheBeforeSave(){
441
442 DistributedCacheManagerDecorator distributedCacheManagerDecorator =
443 GlobalResourceLoader.getService(KrmsConstants.KRMS_DISTRIBUTED_CACHE);
444
445 distributedCacheManagerDecorator.getCache(ActionDefinition.Cache.NAME).clear();
446 distributedCacheManagerDecorator.getCache(AgendaItemDefinition.Cache.NAME).clear();
447 distributedCacheManagerDecorator.getCache(AgendaTreeDefinition.Cache.NAME).clear();
448 distributedCacheManagerDecorator.getCache(AgendaDefinition.Cache.NAME).clear();
449 distributedCacheManagerDecorator.getCache(ContextDefinition.Cache.NAME).clear();
450 distributedCacheManagerDecorator.getCache(KrmsAttributeDefinition.Cache.NAME).clear();
451 distributedCacheManagerDecorator.getCache(KrmsTypeDefinition.Cache.NAME).clear();
452 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
453 distributedCacheManagerDecorator.getCache(PropositionDefinition.Cache.NAME).clear();
454 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
455 distributedCacheManagerDecorator.getCache(TermDefinition.Cache.NAME).clear();
456 distributedCacheManagerDecorator.getCache(TermResolverDefinition.Cache.NAME).clear();
457 distributedCacheManagerDecorator.getCache(TermSpecificationDefinition.Cache.NAME).clear();
458 }
459
460
461
462
463
464
465 private void saveNewParameterizedTerms(PropositionBo propositionBo) {
466 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
467
468 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(0).getValue().startsWith(
469 KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
470 String termId = propositionBo.getParameters().get(0).getValue();
471 String termSpecId = termId.substring(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length());
472
473 TermBo newTerm = new TermBo();
474 newTerm.setDescription(propositionBo.getNewTermDescription());
475 newTerm.setSpecificationId(termSpecId);
476 newTerm.setId(termIdIncrementer.getNewId());
477
478 List<TermParameterBo> params = new ArrayList<TermParameterBo>();
479 for (Map.Entry<String, String> entry : propositionBo.getTermParameters().entrySet()) {
480 TermParameterBo param = new TermParameterBo();
481 param.setTerm(newTerm);
482 param.setName(entry.getKey());
483 param.setValue(entry.getValue());
484 param.setId(termParameterIdIncrementer.getNewId());
485
486 params.add(param);
487 }
488
489 newTerm.setParameters(params);
490
491 getLegacyDataAdapter().linkAndSave(newTerm);
492 propositionBo.getParameters().get(0).setValue(newTerm.getId());
493 }
494 } else {
495
496 for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
497 saveNewParameterizedTerms(childProp);
498 }
499 }
500 }
501
502
503
504
505
506
507 private void processCustomOperators(PropositionBo propositionBo) {
508 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
509
510 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(2).getValue().startsWith(
511 KrmsImplConstants.CUSTOM_OPERATOR_PREFIX)) {
512 PropositionParameterBo operatorParam = propositionBo.getParameters().get(2);
513
514 CustomOperator customOperator =
515 KrmsServiceLocatorInternal.getCustomOperatorUiTranslator().getCustomOperator(operatorParam.getValue());
516
517 FunctionDefinition operatorFunctionDefinition = customOperator.getOperatorFunctionDefinition();
518
519 operatorParam.setParameterType(PropositionParameterType.FUNCTION.getCode());
520 operatorParam.setValue(operatorFunctionDefinition.getId());
521 }
522 } else {
523
524 for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
525 processCustomOperators(childProp);
526 }
527 }
528 }
529
530
531
532
533
534
535
536
537 private Map<String, KrmsAttributeDefinition> buildAttributeDefinitionMap(String agendaTypeId) {
538 KrmsAttributeDefinitionService attributeDefinitionService =
539 KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
540
541
542 Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
543
544 List<KrmsAttributeDefinition> attributeDefinitions = attributeDefinitionService.findAttributeDefinitionsByType(
545 agendaTypeId);
546
547 for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
548 attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
549 }
550 return attributeDefinitionMap;
551 }
552
553 @Override
554 public boolean isOldDataObjectInDocument() {
555 boolean isOldDataObjectInExistence = true;
556
557 if (getDataObject() == null) {
558 isOldDataObjectInExistence = false;
559 } else {
560
561 Map<String, ?> keyFieldValues = getLegacyDataAdapter().getPrimaryKeyFieldValues(
562 ((AgendaEditor) getDataObject()).getAgenda());
563 for (Object keyValue : keyFieldValues.values()) {
564 if (keyValue == null) {
565 isOldDataObjectInExistence = false;
566 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
567 isOldDataObjectInExistence = false;
568 }
569
570 if (!isOldDataObjectInExistence) {
571 break;
572 }
573 }
574 }
575
576 return isOldDataObjectInExistence;
577 }
578
579
580 @Override
581 public Class getDataObjectClass() {
582 return AgendaBo.class;
583 }
584
585 @Override
586 public boolean isLockable() {
587 return true;
588 }
589
590 @Override
591 public void processBeforeAddLine(ViewModel model, Object addLine, String collectionId, String collectionPath) {
592 AgendaEditor agendaEditor = getAgendaEditor(model);
593 if (addLine instanceof ActionBo) {
594 ((ActionBo) addLine).setNamespace(agendaEditor.getAgendaItemLine().getRule().getNamespace());
595 }
596
597 super.processBeforeAddLine(model, addLine, collectionId, collectionPath);
598 }
599 }