View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.ojb.broker.OptimisticLockException;
20  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
21  import org.kuali.rice.core.api.util.tree.Node;
22  import org.kuali.rice.core.api.util.tree.Tree;
23  import org.kuali.rice.krad.bo.Note;
24  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
25  import org.kuali.rice.krad.uif.container.CollectionGroup;
26  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
27  import org.kuali.rice.krad.uif.view.View;
28  import org.kuali.rice.krad.util.KRADConstants;
29  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
30  import org.kuali.rice.krms.api.KrmsConstants;
31  import org.kuali.rice.krms.api.repository.RuleManagementService;
32  import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
33  import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
34  import org.kuali.rice.krms.api.repository.context.ContextDefinition;
35  import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplate;
36  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
37  import org.kuali.rice.krms.api.repository.reference.ReferenceObjectBinding;
38  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
39  import org.kuali.rice.krms.api.repository.term.TermDefinition;
40  import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
41  import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
42  import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
43  import org.kuali.rice.krms.api.repository.typerelation.TypeTypeRelation;
44  import org.kuali.rice.krms.dto.*;
45  import org.kuali.rice.krms.builder.ComponentBuilder;
46  import org.kuali.rice.krms.service.TemplateRegistry;
47  import org.kuali.rice.krms.tree.RuleCompareTreeBuilder;
48  import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
49  import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
50  import org.kuali.rice.krms.service.RuleEditorMaintainable;
51  import org.kuali.rice.krms.util.AlphaIterator;
52  import org.kuali.student.common.uif.service.impl.KSMaintainableImpl;
53  import org.kuali.rice.krms.util.PropositionTreeUtil;
54  import org.kuali.student.common.krms.exceptions.KRMSOptimisticLockingException;
55  import org.kuali.student.r1.common.rice.StudentIdentityConstants;
56  import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
57  import org.springmodules.orm.ojb.OjbOperationException;
58  
59  import javax.xml.namespace.QName;
60  import java.util.*;
61  
62  /**
63   * {@link org.kuali.rice.krad.maintenance.Maintainable}
64   *
65   * @author Kuali Student Team (rice.collab@kuali.org)
66   */
67  public class RuleEditorMaintainableImpl extends KSMaintainableImpl implements RuleEditorMaintainable {
68  
69      private static final long serialVersionUID = 1L;
70  
71      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RuleEditorMaintainableImpl.class);
72  
73      private transient RuleManagementService ruleManagementService;
74      private transient KrmsTypeRepositoryService krmsTypeRepositoryService;
75      private transient TermRepositoryService termRepositoryService;
76  
77      private transient TemplateRegistry templateRegistry;
78      private AlphaIterator alphaIterator = new AlphaIterator(StringUtils.EMPTY);
79  
80      public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document";
81  
82      public String getViewTypeName() {
83          return "kuali.krms.agenda.type";
84      }
85  
86      /**
87       * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject
88       *
89       * @param model the MaintenanceDocumentForm
90       * @return the AgendaEditor
91       */
92      private RuleEditor getRuleEditor(Object model) {
93          MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
94          return (RuleEditor) maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject();
95      }
96  
97      @Override
98      public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
99          RuleManagementWrapper dataObject = new RuleManagementWrapper();
100 
101         String refObjectId = dataObjectKeys.get("refObjectId");
102         dataObject.setRefObjectId(refObjectId);
103 
104         dataObject.setAgendas(this.getAgendasForRef("", refObjectId, null));
105 
106         dataObject.setCompareTree(RuleCompareTreeBuilder.initCompareTree());
107 
108         return dataObject;
109     }
110 
111     protected List<AgendaEditor> getAgendasForRef(String discriminatorType, String refObjectId, String parentRefObjectId) {
112         // Initialize new array lists.
113         List<AgendaEditor> agendas = new ArrayList<AgendaEditor>();
114         List<AgendaEditor> sortedAgendas = new ArrayList<AgendaEditor>();
115         List<AgendaEditor> parentAgendas = new ArrayList<AgendaEditor>();
116 
117         // Get the list of existing agendas
118         LOG.info("Retrieving reference object binding for refobjectid: " + refObjectId);
119         List<ReferenceObjectBinding> refObjectsBindings = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(discriminatorType, refObjectId);
120         for (ReferenceObjectBinding referenceObjectBinding : refObjectsBindings) {
121             LOG.info("Retrieved reference object binding with id: " + referenceObjectBinding);
122             agendas.add(this.getAgendaEditor(referenceObjectBinding.getKrmsObjectId()));
123         }
124 
125         // Get the list of parent agendas
126         List<ReferenceObjectBinding> parentRefObjects = this.getParentRefOjbects(parentRefObjectId);
127         for (ReferenceObjectBinding referenceObject : parentRefObjects) {
128             parentAgendas.add(this.getAgendaEditor(referenceObject.getKrmsObjectId()));
129         }
130 
131         // Lookup existing agenda by type
132         for (AgendaTypeInfo agendaTypeInfo : this.getTypeRelationships()) {
133             AgendaEditor agenda = null;
134             for (AgendaEditor existingAgenda : agendas) {
135                 if (existingAgenda.getTypeId().equals(agendaTypeInfo.getId())) {
136                     agenda = existingAgenda;
137                     break;
138                 }
139             }
140             if (agenda == null) {
141                 agenda = new AgendaEditor();
142                 agenda.setTypeId(agendaTypeInfo.getId());
143             }
144 
145             //Set the parent agenda.
146             for (AgendaEditor parent : parentAgendas) {
147                 if (agenda.getTypeId().equals(agenda.getTypeId())) {
148                     agenda.setParent(parent);
149                     break;
150                 }
151             }
152 
153             agenda.setAgendaTypeInfo(agendaTypeInfo);
154             agenda.setRuleEditors(this.getRulesForAgendas(agenda));
155             sortedAgendas.add(agenda);
156         }
157 
158         return sortedAgendas;
159     }
160 
161     protected AgendaEditor getAgendaEditor(String agendaId) {
162         LOG.info("Retrieving agenda for id: " + agendaId);
163         AgendaDefinition agenda = this.getRuleManagementService().getAgenda(agendaId);
164         return new AgendaEditor(agenda);
165     }
166 
167     public Map<String, RuleEditor> getRulesForAgendas(AgendaEditor agenda) {
168 
169         //Get all existing rules.
170         List<RuleEditor> existingRules = null;
171         if (agenda.getId() != null) {
172             LOG.info("Retrieving agenda item for id: " + agenda.getFirstItemId());
173             AgendaItemDefinition firstItem = this.getRuleManagementService().getAgendaItem(agenda.getFirstItemId());
174             existingRules = getRuleEditorsFromTree(firstItem, true);
175         }
176 
177         //Get the parent rules
178         List<RuleEditor> parentRules = null;
179         if (agenda.getParent() != null) {
180             AgendaItemDefinition parentItem = this.getRuleManagementService().getAgendaItem(agenda.getParent().getFirstItemId());
181             parentRules = getRuleEditorsFromTree(parentItem, false);
182         }
183 
184         //Add dummy RuleEditors for empty rule types.
185         Map<String, RuleEditor> ruleEditors = new LinkedHashMap<String, RuleEditor>();
186         for (RuleTypeInfo ruleType : agenda.getAgendaTypeInfo().getRuleTypes()) {
187             RuleEditor ruleEditor = null;
188 
189             // Add all existing rules of this type.
190             if (existingRules != null) {
191                 for (RuleEditor rule : existingRules) {
192                     if (rule.getTypeId().equals(ruleType.getId()) && (!rule.isDummy())) {
193                         ruleEditor = rule;
194                     }
195                 }
196             }
197 
198             // If the ruletype does not exist, add an empty rule section
199             if (ruleEditor == null) {
200                 ruleEditor = createDummyRuleEditor(ruleType.getId());
201             }
202 
203             ruleEditor.setKey((String) alphaIterator.next());
204             ruleEditor.setRuleTypeInfo(ruleType);
205             ruleEditors.put(ruleEditor.getKey(), ruleEditor);
206 
207             //Set the parent agenda.
208             if (parentRules != null) {
209                 for (RuleEditor parent : parentRules) {
210                     if (ruleEditor.getTypeId().equals(parent.getTypeId())) {
211                         ruleEditor.setParent(parent);
212                         break;
213                     }
214                 }
215             }
216         }
217 
218         return ruleEditors;
219     }
220 
221     protected RuleEditor createDummyRuleEditor(String ruleTypeId) {
222         RuleEditor ruleEditor = new RuleEditor();
223         ruleEditor.setDummy(true);
224         ruleEditor.setTypeId(ruleTypeId);
225         return ruleEditor;
226     }
227 
228     protected List<RuleEditor> getRuleEditorsFromTree(AgendaItemDefinition agendaItem, boolean initProps) {
229 
230         List<RuleEditor> rules = new ArrayList<RuleEditor>();
231         if (agendaItem.getRule() != null) {
232             RuleEditor ruleEditor = new RuleEditor(agendaItem.getRule());
233             if (initProps) {
234                 this.initPropositionEditor(ruleEditor.getPropositionEditor());
235                 ruleEditor.setViewTree(this.getViewTreeBuilder().buildTree(ruleEditor));
236             }
237             rules.add(ruleEditor);
238         }
239 
240         if (agendaItem.getWhenTrue() != null) {
241             rules.addAll(getRuleEditorsFromTree(agendaItem.getWhenTrue(), initProps));
242         }
243 
244         return rules;
245     }
246 
247     /**
248      * Override this method to return the reference object id of the parent object.
249      *
250      * @param parentRefObjectId
251      * @return
252      */
253     @Override
254     public List<ReferenceObjectBinding> getParentRefOjbects(String parentRefObjectId) {
255         return null;
256     }
257 
258     protected RuleViewTreeBuilder getViewTreeBuilder() {
259         return new RuleViewTreeBuilder();
260     }
261 
262     /**
263      * Setup a map with all the type information required to build an agenda management page.
264      *
265      * @return
266      */
267     protected List<AgendaTypeInfo> getTypeRelationships() {
268         List<AgendaTypeInfo> agendaTypeInfos = new ArrayList<AgendaTypeInfo>();
269 
270         // Get Instruction Usage Id
271         String instructionUsageId = getRuleManagementService().getNaturalLanguageUsageByNameAndNamespace(KSKRMSServiceConstants.KRMS_NL_TYPE_INSTRUCTION,
272                 StudentIdentityConstants.KS_NAMESPACE_CD).getId();
273 
274         // Get Description Usage Id
275         String descriptionUsageId = getRuleManagementService().getNaturalLanguageUsageByNameAndNamespace(KSKRMSServiceConstants.KRMS_NL_TYPE_DESCRIPTION,
276                 StudentIdentityConstants.KS_NAMESPACE_CD).getId();
277 
278         // Get the super type.
279         KrmsTypeDefinition requisitesType = this.getKrmsTypeRepositoryService().getTypeByName(StudentIdentityConstants.KS_NAMESPACE_CD, this.getViewTypeName());
280 
281         // Get all agenda types linked to super type.
282         List<TypeTypeRelation> agendaRelationships = this.getSortedTypeRelationshipsForTypeId(requisitesType.getId());
283         for (TypeTypeRelation agendaRelationship : agendaRelationships) {
284             AgendaTypeInfo agendaTypeInfo = new AgendaTypeInfo();
285             agendaTypeInfo.setId(agendaRelationship.getToTypeId());
286             KrmsTypeDefinition agendaType = this.getKrmsTypeRepositoryService().getTypeById(agendaTypeInfo.getId());
287             agendaTypeInfo.setType(agendaType.getName());
288             agendaTypeInfo.setDescription(this.getDescriptionForTypeAndUsage(agendaRelationship.getToTypeId(), descriptionUsageId));
289 
290             List<RuleTypeInfo> ruleTypes = new ArrayList<RuleTypeInfo>();
291             List<TypeTypeRelation> sortedRuleRelationships = this.getSortedTypeRelationshipsForTypeId(agendaRelationship.getToTypeId());
292             for (TypeTypeRelation ruleRelationship : sortedRuleRelationships) {
293                 RuleTypeInfo ruleTypeInfo = new RuleTypeInfo();
294                 ruleTypeInfo.setId(ruleRelationship.getToTypeId());
295                 KrmsTypeDefinition ruleType = this.getKrmsTypeRepositoryService().getTypeById(ruleTypeInfo.getId());
296                 ruleTypeInfo.setType(ruleType.getName());
297                 ruleTypeInfo.setDescription(this.getDescriptionForTypeAndUsage(ruleRelationship.getToTypeId(), descriptionUsageId));
298                 if (ruleTypeInfo.getDescription().isEmpty()) {
299                     ruleTypeInfo.setDescription("Description is unset rule type");
300                 }
301                 ruleTypeInfo.setInstruction(this.getDescriptionForTypeAndUsage(ruleRelationship.getToTypeId(), instructionUsageId));
302                 if (ruleTypeInfo.getInstruction().isEmpty()) {
303                     ruleTypeInfo.setInstruction("Instruction is unset for rule type");
304                 }
305                 // Add rule types to list.
306                 ruleTypes.add(ruleTypeInfo);
307             }
308             agendaTypeInfo.setRuleTypes(ruleTypes);
309             agendaTypeInfos.add(agendaTypeInfo);
310         }
311 
312         return agendaTypeInfos;
313     }
314 
315     private List<TypeTypeRelation> getSortedTypeRelationshipsForTypeId(String typeId){
316         // Get all rule types for each agenda type
317         List<TypeTypeRelation> relationships = new ArrayList<TypeTypeRelation>();
318         relationships.addAll(this.getKrmsTypeRepositoryService().findTypeTypeRelationsByFromType(typeId));
319 
320         // order rules
321         Collections.sort(relationships, new Comparator<TypeTypeRelation>() {
322             @Override
323             public int compare(TypeTypeRelation typeTypeRelation1, TypeTypeRelation typeTypeRelation2) {
324                 return typeTypeRelation1.getSequenceNumber().compareTo(typeTypeRelation2.getSequenceNumber());
325             }
326         });
327         return relationships;
328     }
329 
330     private String getDescriptionForTypeAndUsage(String typeId, String usageId) {
331         NaturalLanguageTemplate template = null;
332         try {
333             template = getRuleManagementService().findNaturalLanguageTemplateByLanguageCodeTypeIdAndNluId("en", typeId, usageId);
334             return template.getTemplate();
335         } catch (Exception e) {
336             return StringUtils.EMPTY;
337         }
338     }
339 
340     /**
341      * {@inheritDoc}
342      */
343     @Override
344     public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
345         super.processAfterNew(document, requestParameters);
346         document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
347     }
348 
349     @Override
350     public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
351         super.processAfterEdit(document, requestParameters);
352         document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document");
353     }
354 
355     @Override
356     public void saveDataObject() {
357         RuleManagementWrapper ruleWrapper = (RuleManagementWrapper) getDataObject();
358 
359         for (AgendaEditor agenda : ruleWrapper.getAgendas()) {
360 
361             //Check if this agenda has anything to save
362             if (agenda.isDummyAgenda()) {
363                 continue;
364             }
365 
366             //Set the agenda name.
367             agenda.setName(ruleWrapper.getRefObjectId() + ":" + agenda.getAgendaTypeInfo().getId() + ":1");
368 
369             //Retrieve the context and set the id on the agenda.
370             if (agenda.getContextId() == null) {
371                 ContextDefinition context = this.getRuleManagementService().getContextByNameAndNamespace("Course Requirements", ruleWrapper.getNamespace());
372                 agenda.setContextId(context.getId());
373             }
374 
375             //Create or update the agenda.
376             if (agenda.getId() == null) {
377 
378                 //Check if someone else has not created an agenda while this one was created.
379                 if(this.getRuleManagementService().getAgendaByNameAndContextId(agenda.getName(), agenda.getContextId())!=null){
380                     throw new KRMSOptimisticLockingException();
381                 }
382 
383                 AgendaDefinition.Builder agendaBldr = AgendaDefinition.Builder.create(agenda);
384                 AgendaDefinition agendaDfn = this.getRuleManagementService().createAgenda(agendaBldr.build());
385 
386                 //Set the id and versionnumber for a possible update.
387                 agenda.setId(agendaDfn.getId());
388                 agenda.setVersionNumber(agendaDfn.getVersionNumber());
389                 agenda.setFirstItemId(agendaDfn.getFirstItemId());
390 
391                 //Create the reference object binding only on create agenda, no need to update.
392                 ReferenceObjectBinding.Builder refBuilder = ReferenceObjectBinding.Builder.create("Agenda",
393                         agendaDfn.getId(), ruleWrapper.getNamespace(), ruleWrapper.getRefDiscriminatorType(), ruleWrapper.getRefObjectId());
394                 this.getRuleManagementService().createReferenceObjectBinding(refBuilder.build());
395             }
396 
397             //Update the root item.
398             try {
399 
400                 //Set the first agenda item id and save the agenda items
401                 AgendaItemDefinition firstItem = maintainAgendaItems(agenda, ruleWrapper.getRefObjectId() + ":", ruleWrapper.getNamespace());
402 
403                 //If no more rules linked to agenda, delete it.
404                 if (firstItem.getRule() == null) {
405                     List<ReferenceObjectBinding> refObjectsBindings = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(ruleWrapper.getRefDiscriminatorType(), ruleWrapper.getRefObjectId());
406                     for (ReferenceObjectBinding referenceObjectBinding : refObjectsBindings) {
407                         if (referenceObjectBinding.getKrmsObjectId().equals(agenda.getId())) {
408                             LOG.info("Deleting reference object binding for id: " + referenceObjectBinding.getId());
409                             this.getRuleManagementService().deleteReferenceObjectBinding(referenceObjectBinding.getId());
410                         }
411                     }
412                     this.getRuleManagementService().deleteAgenda(agenda.getId());
413                 }
414 
415 
416             } catch (OjbOperationException e) {
417                 //OptimisticLockException
418                 if (e.getCause() instanceof OptimisticLockException) {
419                     throw new KRMSOptimisticLockingException();
420                 } else {
421                     throw e;
422                 }
423             }
424 
425         }
426 
427     }
428 
429     public AgendaItemDefinition maintainAgendaItems(AgendaEditor agenda, String namePrefix, String nameSpace) {
430 
431         Queue<RuleDefinition.Builder> rules = new LinkedList<RuleDefinition.Builder>();
432         for (RuleEditor rule : agenda.getRuleEditors().values()) {
433             if (!rule.isDummy()) {
434                 rules.add(this.finRule(rule, namePrefix, nameSpace));
435             }
436         }
437 
438         // Clear the first item and update.
439         AgendaItemDefinition firstItem = manageFirstItem(agenda);
440 
441         //Delete rules
442         for (RuleEditor deletedRule : agenda.getDeletedRules()) {
443             this.getRuleManagementService().deleteRule(deletedRule.getId());
444         }
445 
446         AgendaItemDefinition.Builder rootItemBuilder = AgendaItemDefinition.Builder.create(firstItem);
447         AgendaItemDefinition.Builder itemBuilder = rootItemBuilder;
448         while (rules.peek() != null) {
449             itemBuilder.setRule(rules.poll());
450             itemBuilder.setRuleId(itemBuilder.getRule().getId());
451             if (rules.peek() != null) {
452                 itemBuilder.setWhenTrue(AgendaItemDefinition.Builder.create(null, agenda.getId()));
453                 itemBuilder = itemBuilder.getWhenTrue();
454             }
455         }
456 
457         //Update the root item.
458         AgendaItemDefinition updateItem = rootItemBuilder.build();
459         this.getRuleManagementService().updateAgendaItem(updateItem);
460 
461         return updateItem;
462     }
463 
464     protected AgendaItemDefinition manageFirstItem(AgendaEditor agenda) {
465         AgendaItemDefinition firstItem;
466         AgendaItemDefinition.Builder firstItemBuilder = AgendaItemDefinition.Builder.create(agenda.getFirstItemId(), agenda.getId());
467         if (agenda.getFirstItemId() != null) {
468             //Retrieve the first item from the database.
469             firstItem = this.getRuleManagementService().getAgendaItem(agenda.getFirstItemId());
470             if(firstItem==null){
471                 throw new KRMSOptimisticLockingException("Rule was deleted by another user.");
472             }
473 
474             firstItemBuilder.setVersionNumber(firstItem.getVersionNumber());
475             this.getRuleManagementService().updateAgendaItem(firstItemBuilder.build());
476 
477             //Delete current agenda items to rebuild the tree.
478             this.deleteAgendaItems(firstItem.getWhenTrue());
479             this.deleteAgendaItems(firstItem.getWhenFalse());
480             this.deleteAgendaItems(firstItem.getAlways());
481         } else {
482             firstItem = this.getRuleManagementService().createAgendaItem(firstItemBuilder.build());
483             agenda.setFirstItemId(firstItem.getId());
484             AgendaDefinition.Builder agendaBldr = AgendaDefinition.Builder.create(agenda);
485             this.getRuleManagementService().updateAgenda(agendaBldr.build());
486         }
487         return this.getRuleManagementService().getAgendaItem(agenda.getFirstItemId());
488     }
489 
490     public void deleteAgendaItems(AgendaItemDefinition agendaItem) {
491         if (agendaItem != null) {
492             this.getRuleManagementService().deleteAgendaItem(agendaItem.getId());
493             deleteAgendaItems(agendaItem.getWhenFalse());
494             deleteAgendaItems(agendaItem.getWhenTrue());
495             deleteAgendaItems(agendaItem.getAlways());
496         }
497     }
498 
499     public RuleDefinition.Builder finRule(RuleEditor rule, String rulePrefix, String namespace) {
500         // handle saving new parameterized terms
501         if (rule.getPropositionEditor() != null) {
502             this.onSubmit(rule.getPropositionEditor());
503         }
504 
505         if (rule.getNamespace() == null) {
506             rule.setNamespace(namespace);
507         }
508         rule.setName(rulePrefix + rule.getRuleTypeInfo().getId() + ":1");
509 
510         //Check if someone else has not created a rule while this one was created.
511         if(rule.getId()==null){
512             if(this.getRuleManagementService().getRuleByNameAndNamespace(rule.getName(), rule.getNamespace())!=null){
513                 throw new KRMSOptimisticLockingException();
514             }
515         }
516 
517         return RuleDefinition.Builder.create(rule);
518     }
519 
520     public void onSubmit(PropositionEditor propositionEditor) {
521         if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
522 
523             //Call onsubmit on the associated builder.
524             ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType());
525             if (builder != null) {
526                 builder.onSubmit(propositionEditor);
527             }
528 
529         } else {
530 
531             //If not a simple node, recursively finalize the child proposition editors.
532             for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
533                 onSubmit(child);
534             }
535 
536         }
537     }
538 
539     public void initPropositionEditor(PropositionEditor propositionEditor) {
540         if (propositionEditor == null) {
541             return;
542         }
543 
544         if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
545 
546             if (propositionEditor.getType() == null) {
547                 KrmsTypeDefinition type = this.getKrmsTypeRepositoryService().getTypeById(propositionEditor.getTypeId());
548                 propositionEditor.setType(type.getName());
549             }
550 
551             Map<String, String> termParameters = this.getTermParameters(propositionEditor);
552             ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType());
553             if (builder != null) {
554                 builder.resolveTermParameters(propositionEditor, termParameters);
555             }
556         } else {
557             for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
558                 initPropositionEditor(child);
559             }
560 
561         }
562     }
563 
564     protected Map<String, String> getTermParameters(PropositionEditor proposition) {
565 
566         Map<String, String> termParameters = new HashMap<String, String>();
567         if (proposition.getTerm() == null) {
568             PropositionParameterEditor termParameter = PropositionTreeUtil.getTermParameter(proposition.getParameters());
569             if (termParameter != null) {
570 
571                 if (termParameter.getTermValue() == null) {
572                     proposition.setTerm(new TermEditor());
573                 } else {
574                     proposition.setTerm(new TermEditor(termParameter.getTermValue()));
575                 }
576 
577             } else {
578                 return termParameters;
579             }
580         }
581 
582         for (TermParameterEditor parameter : proposition.getTerm().getEditorParameters()) {
583             termParameters.put(parameter.getName(), parameter.getValue());
584         }
585 
586         return termParameters;
587     }
588 
589     /**
590      * In the case of edit maintenance adds a new blank line to the old side
591      * <p/>
592      *
593      * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View,
594      *      org.kuali.rice.krad.uif.container.CollectionGroup, Object,
595      *      Object, boolean)
596      */
597     @Override
598     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine, boolean isValidLine) {
599         // Check for maintenance documents in edit but exclude notes
600         if (model instanceof MaintenanceDocumentForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm) model).getMaintenanceAction()) && !(addLine instanceof Note)) {
601 
602             // Figure out which rule is being edited
603             RuleEditor rule = getRuleEditor(model);
604             // Figure out which proposition is being edited
605             Tree<RuleEditorTreeNode, String> propositionTree = rule.getEditTree();
606             Node<RuleEditorTreeNode, String> editedPropositionNode = PropositionTreeUtil.findEditedProposition(propositionTree.getRootElement());
607 
608             // get the old object's collection
609             Collection<Object> oldCollection = ObjectPropertyUtils
610                     .getPropertyValue(editedPropositionNode.getData(),
611                             collectionGroup.getPropertyName());
612 
613             try {
614                 Object blankLine = collectionGroup.getCollectionObjectClass().newInstance();
615                 //Add a blank line to the top of the collection
616                 if (oldCollection instanceof List) {
617                     ((List) oldCollection).add(0, blankLine);
618                 } else {
619                     oldCollection.add(blankLine);
620                 }
621             } catch (Exception e) {
622                 throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
623             }
624         }
625     }
626 
627     public RuleManagementService getRuleManagementService() {
628         if (ruleManagementService == null) {
629             ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "ruleManagementService"));
630         }
631         return ruleManagementService;
632     }
633 
634     public KrmsTypeRepositoryService getKrmsTypeRepositoryService() {
635         if (krmsTypeRepositoryService == null) {
636             krmsTypeRepositoryService = (KrmsTypeRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "krmsTypeRepositoryService"));
637         }
638         return krmsTypeRepositoryService;
639     }
640 
641     public TermRepositoryService getTermRepositoryService() {
642         if (termRepositoryService == null) {
643             termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService"));
644         }
645         return termRepositoryService;
646     }
647 
648     private TemplateRegistry getTemplateRegistry() {
649         if (templateRegistry == null) {
650             templateRegistry = (TemplateRegistry) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/templateResolverService", "templateResolverService"));
651         }
652         return templateRegistry;
653     }
654 }