001/**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krms.service.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.ojb.broker.OptimisticLockException;
020import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
021import org.kuali.rice.core.api.util.tree.Node;
022import org.kuali.rice.core.api.util.tree.Tree;
023import org.kuali.rice.krad.bo.Note;
024import org.kuali.rice.krad.maintenance.MaintenanceDocument;
025import org.kuali.rice.krad.uif.UifConstants;
026import org.kuali.rice.krad.uif.component.BindingInfo;
027import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
028import org.kuali.rice.krad.uif.view.ViewModel;
029import org.kuali.rice.krad.util.KRADConstants;
030import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
031import org.kuali.rice.krms.api.KrmsConstants;
032import org.kuali.rice.krms.api.repository.RuleManagementService;
033import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
034import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
035import org.kuali.rice.krms.api.repository.context.ContextDefinition;
036import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplate;
037import org.kuali.rice.krms.api.repository.proposition.PropositionType;
038import org.kuali.rice.krms.api.repository.reference.ReferenceObjectBinding;
039import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
040import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
041import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
042import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
043import org.kuali.rice.krms.api.repository.typerelation.TypeTypeRelation;
044import org.kuali.rice.krms.builder.ComponentBuilder;
045import org.kuali.rice.krms.dto.AgendaEditor;
046import org.kuali.rice.krms.dto.AgendaTypeInfo;
047import org.kuali.rice.krms.dto.PropositionEditor;
048import org.kuali.rice.krms.dto.PropositionParameterEditor;
049import org.kuali.rice.krms.dto.RuleEditor;
050import org.kuali.rice.krms.dto.RuleManagementWrapper;
051import org.kuali.rice.krms.dto.RuleManager;
052import org.kuali.rice.krms.dto.RuleTypeInfo;
053import org.kuali.rice.krms.dto.TermEditor;
054import org.kuali.rice.krms.dto.TermParameterEditor;
055import org.kuali.rice.krms.service.RuleEditorMaintainable;
056import org.kuali.rice.krms.service.TemplateRegistry;
057import org.kuali.rice.krms.tree.RuleCompareTreeBuilder;
058import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
059import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
060import org.kuali.rice.krms.util.AlphaIterator;
061import org.kuali.rice.krms.util.PropositionTreeUtil;
062import org.kuali.student.common.krms.exceptions.KRMSOptimisticLockingException;
063import org.kuali.student.common.uif.service.impl.KSMaintainableImpl;
064import org.kuali.student.r1.common.rice.StudentIdentityConstants;
065import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
066import org.slf4j.Logger;
067import org.slf4j.LoggerFactory;
068import org.springmodules.orm.ojb.OjbOperationException;
069
070import javax.xml.namespace.QName;
071import java.util.ArrayList;
072import java.util.Collection;
073import java.util.Collections;
074import java.util.Comparator;
075import java.util.HashMap;
076import java.util.LinkedHashMap;
077import java.util.LinkedList;
078import java.util.List;
079import java.util.Map;
080import java.util.Queue;
081
082/**
083 * {@link org.kuali.rice.krad.maintenance.Maintainable}
084 *
085 * @author Kuali Student Team (rice.collab@kuali.org)
086 */
087public class RuleEditorMaintainableImpl extends KSMaintainableImpl implements RuleEditorMaintainable {
088
089    private static final long serialVersionUID = 1L;
090
091    private static final Logger LOG = LoggerFactory.getLogger(RuleEditorMaintainableImpl.class);
092
093    private transient RuleManagementService ruleManagementService;
094    private transient KrmsTypeRepositoryService krmsTypeRepositoryService;
095    private transient TermRepositoryService termRepositoryService;
096
097    private transient TemplateRegistry templateRegistry;
098    private AlphaIterator alphaIterator = new AlphaIterator(StringUtils.EMPTY);
099
100    public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document";
101
102    public String getViewTypeName() {
103        return "kuali.krms.agenda.type";
104    }
105
106    /**
107     * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject
108     *
109     * @param model the MaintenanceDocumentForm
110     * @return the AgendaEditor
111     */
112    private RuleEditor getRuleEditor(Object model) {
113        MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
114        return (RuleEditor) maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject();
115    }
116
117    @Override
118    public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
119        RuleManager dataObject = new RuleManagementWrapper();
120
121        String refObjectId = dataObjectKeys.get("refObjectId");
122        dataObject.setRefObjectId(refObjectId);
123
124        dataObject.setAgendas(this.getAgendasForRef("", refObjectId, null));
125
126        dataObject.setCompareTree(RuleCompareTreeBuilder.initCompareTree());
127
128        return dataObject;
129    }
130
131    protected List<AgendaEditor> getAgendasForRef(String discriminatorType, String refObjectId, String parentRefObjectId) {
132        // Initialize new array lists.
133        List<AgendaEditor> agendas = new ArrayList<AgendaEditor>();
134        List<AgendaEditor> sortedAgendas = new ArrayList<AgendaEditor>();
135        List<AgendaEditor> parentAgendas = new ArrayList<AgendaEditor>();
136
137        // Get the list of existing agendas
138        LOG.info("Retrieving reference object binding for refobjectid: {}", refObjectId);
139        List<ReferenceObjectBinding> refObjectsBindings = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(discriminatorType, refObjectId);
140        for (ReferenceObjectBinding referenceObjectBinding : refObjectsBindings) {
141            LOG.info("Retrieved reference object binding with id: {}", referenceObjectBinding.getReferenceObjectId());
142            agendas.add(this.getAgendaEditor(referenceObjectBinding.getKrmsObjectId()));
143        }
144
145        // Get the list of parent agendas
146        List<ReferenceObjectBinding> parentRefObjects = this.getParentRefOjbects(parentRefObjectId);
147        for (ReferenceObjectBinding referenceObject : parentRefObjects) {
148            parentAgendas.add(this.getAgendaEditor(referenceObject.getKrmsObjectId()));
149        }
150
151        // Lookup existing agenda by type
152        for (AgendaTypeInfo agendaTypeInfo : this.getTypeRelationships()) {
153            AgendaEditor agenda = null;
154            for (AgendaEditor existingAgenda : agendas) {
155                if (existingAgenda.getTypeId().equals(agendaTypeInfo.getId())) {
156                    agenda = existingAgenda;
157                    break;
158                }
159            }
160            if (agenda == null) {
161                agenda = new AgendaEditor();
162                agenda.setTypeId(agendaTypeInfo.getId());
163            }
164
165            //Set the parent agenda.
166            for (AgendaEditor parent : parentAgendas) {
167                if (agenda.getTypeId().equals(agenda.getTypeId())) {
168                    agenda.setParent(parent);
169                    break;
170                }
171            }
172
173            agenda.setAgendaTypeInfo(agendaTypeInfo);
174            agenda.setRuleEditors(this.getRulesForAgendas(agenda));
175            sortedAgendas.add(agenda);
176        }
177
178        return sortedAgendas;
179    }
180
181    protected AgendaEditor getAgendaEditor(String agendaId) {
182        LOG.info("Retrieving agenda for id: {}", agendaId);
183        AgendaDefinition agenda = this.getRuleManagementService().getAgenda(agendaId);
184        return new AgendaEditor(agenda);
185    }
186
187    public Map<String, RuleEditor> getRulesForAgendas(AgendaEditor agenda) {
188
189        //Get all existing rules.
190        List<RuleEditor> existingRules = null;
191        if (agenda.getId() != null) {
192            LOG.info("Retrieving agenda item for id: {}", agenda.getFirstItemId());
193            AgendaItemDefinition firstItem = this.getRuleManagementService().getAgendaItem(agenda.getFirstItemId());
194            existingRules = getRuleEditorsFromTree(firstItem, true);
195        }
196
197        //Get the parent rules
198        List<RuleEditor> parentRules = null;
199        if (agenda.getParent() != null) {
200            AgendaItemDefinition parentItem = this.getRuleManagementService().getAgendaItem(agenda.getParent().getFirstItemId());
201            parentRules = getRuleEditorsFromTree(parentItem, false);
202        }
203
204        //Add dummy RuleEditors for empty rule types.
205        Map<String, RuleEditor> ruleEditors = new LinkedHashMap<String, RuleEditor>();
206        for (RuleTypeInfo ruleType : agenda.getAgendaTypeInfo().getRuleTypes()) {
207            RuleEditor ruleEditor = null;
208
209            // Add all existing rules of this type.
210            if (existingRules != null) {
211                for (RuleEditor rule : existingRules) {
212                    if (rule.getTypeId().equals(ruleType.getId()) && (!rule.isDummy())) {
213                        ruleEditor = rule;
214                    }
215                }
216            }
217
218            // If the ruletype does not exist, add an empty rule section
219            if (ruleEditor == null) {
220                ruleEditor = createDummyRuleEditor(ruleType.getId());
221            }
222
223            ruleEditor.setKey((String) alphaIterator.next());
224            ruleEditor.setRuleTypeInfo(ruleType);
225            ruleEditors.put(ruleEditor.getKey(), ruleEditor);
226
227            //Set the parent agenda.
228            if (parentRules != null) {
229                for (RuleEditor parent : parentRules) {
230                    if (ruleEditor.getTypeId().equals(parent.getTypeId())) {
231                        ruleEditor.setParent(parent);
232                        break;
233                    }
234                }
235            }
236        }
237
238        return ruleEditors;
239    }
240
241    protected RuleEditor createDummyRuleEditor(String ruleTypeId) {
242        RuleEditor ruleEditor = new RuleEditor();
243        ruleEditor.setDummy(true);
244        ruleEditor.setTypeId(ruleTypeId);
245        return ruleEditor;
246    }
247
248    protected List<RuleEditor> getRuleEditorsFromTree(AgendaItemDefinition agendaItem, boolean initProps) {
249
250        List<RuleEditor> rules = new ArrayList<RuleEditor>();
251        if (agendaItem.getRule() != null) {
252            RuleEditor ruleEditor = new RuleEditor(agendaItem.getRule());
253            if (initProps) {
254                this.initPropositionEditor(ruleEditor.getPropositionEditor());
255                ruleEditor.setViewTree(this.getViewTreeBuilder().buildTree(ruleEditor));
256            }
257            rules.add(ruleEditor);
258        }
259
260        if (agendaItem.getWhenTrue() != null) {
261            rules.addAll(getRuleEditorsFromTree(agendaItem.getWhenTrue(), initProps));
262        }
263
264        return rules;
265    }
266
267    /**
268     * Override this method to return the reference object id of the parent object.
269     *
270     * @param parentRefObjectId
271     * @return
272     */
273    @Override
274    public List<ReferenceObjectBinding> getParentRefOjbects(String parentRefObjectId) {
275        return null;
276    }
277
278    protected RuleViewTreeBuilder getViewTreeBuilder() {
279        return new RuleViewTreeBuilder();
280    }
281
282    /**
283     * Setup a map with all the type information required to build an agenda management page.
284     *
285     * @return
286     */
287    protected List<AgendaTypeInfo> getTypeRelationships() {
288        List<AgendaTypeInfo> agendaTypeInfos = new ArrayList<AgendaTypeInfo>();
289
290        // Get Instruction Usage Id
291        String instructionUsageId = getRuleManagementService().getNaturalLanguageUsageByNameAndNamespace(KSKRMSServiceConstants.KRMS_NL_TYPE_INSTRUCTION,
292                StudentIdentityConstants.KS_NAMESPACE_CD).getId();
293
294        // Get Description Usage Id
295        String descriptionUsageId = getRuleManagementService().getNaturalLanguageUsageByNameAndNamespace(KSKRMSServiceConstants.KRMS_NL_TYPE_DESCRIPTION,
296                StudentIdentityConstants.KS_NAMESPACE_CD).getId();
297
298        // Get the super type.
299        KrmsTypeDefinition requisitesType = this.getKrmsTypeRepositoryService().getTypeByName(StudentIdentityConstants.KS_NAMESPACE_CD, this.getViewTypeName());
300
301        // Get all agenda types linked to super type.
302        List<TypeTypeRelation> agendaRelationships = this.getSortedTypeRelationshipsForTypeId(requisitesType.getId());
303        for (TypeTypeRelation agendaRelationship : agendaRelationships) {
304            AgendaTypeInfo agendaTypeInfo = new AgendaTypeInfo();
305            agendaTypeInfo.setId(agendaRelationship.getToTypeId());
306            KrmsTypeDefinition agendaType = this.getKrmsTypeRepositoryService().getTypeById(agendaTypeInfo.getId());
307            agendaTypeInfo.setType(agendaType.getName());
308            agendaTypeInfo.setDescription(this.getDescriptionForTypeAndUsage(agendaRelationship.getToTypeId(), descriptionUsageId));
309
310            List<RuleTypeInfo> ruleTypes = new ArrayList<RuleTypeInfo>();
311            List<TypeTypeRelation> sortedRuleRelationships = this.getSortedTypeRelationshipsForTypeId(agendaRelationship.getToTypeId());
312            for (TypeTypeRelation ruleRelationship : sortedRuleRelationships) {
313                RuleTypeInfo ruleTypeInfo = new RuleTypeInfo();
314                ruleTypeInfo.setId(ruleRelationship.getToTypeId());
315                KrmsTypeDefinition ruleType = this.getKrmsTypeRepositoryService().getTypeById(ruleTypeInfo.getId());
316                ruleTypeInfo.setType(ruleType.getName());
317                ruleTypeInfo.setDescription(this.getDescriptionForTypeAndUsage(ruleRelationship.getToTypeId(), descriptionUsageId));
318                if (ruleTypeInfo.getDescription().isEmpty()) {
319                    ruleTypeInfo.setDescription("Description is unset rule type");
320                }
321                ruleTypeInfo.setInstruction(this.getDescriptionForTypeAndUsage(ruleRelationship.getToTypeId(), instructionUsageId));
322                if (ruleTypeInfo.getInstruction().isEmpty()) {
323                    ruleTypeInfo.setInstruction("Instruction is unset for rule type");
324                }
325                // Add rule types to list.
326                ruleTypes.add(ruleTypeInfo);
327            }
328            agendaTypeInfo.setRuleTypes(ruleTypes);
329            agendaTypeInfos.add(agendaTypeInfo);
330        }
331
332        return agendaTypeInfos;
333    }
334
335    private List<TypeTypeRelation> getSortedTypeRelationshipsForTypeId(String typeId){
336        // Get all rule types for each agenda type
337        List<TypeTypeRelation> relationships = new ArrayList<TypeTypeRelation>();
338        relationships.addAll(this.getKrmsTypeRepositoryService().findTypeTypeRelationsByFromType(typeId));
339
340        // order rules
341        Collections.sort(relationships, new Comparator<TypeTypeRelation>() {
342            @Override
343            public int compare(TypeTypeRelation typeTypeRelation1, TypeTypeRelation typeTypeRelation2) {
344                return typeTypeRelation1.getSequenceNumber().compareTo(typeTypeRelation2.getSequenceNumber());
345            }
346        });
347        return relationships;
348    }
349
350    private String getDescriptionForTypeAndUsage(String typeId, String usageId) {
351        NaturalLanguageTemplate template = null;
352        try {
353            template = getRuleManagementService().findNaturalLanguageTemplateByLanguageCodeTypeIdAndNluId("en", typeId, usageId);
354            return template.getTemplate();
355        } catch (Exception e) {
356            return StringUtils.EMPTY;
357        }
358    }
359
360    /**
361     * {@inheritDoc}
362     */
363    @Override
364    public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
365        super.processAfterNew(document, requestParameters);
366        document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
367    }
368
369    @Override
370    public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
371        super.processAfterEdit(document, requestParameters);
372        document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document");
373    }
374
375    @Override
376    public void saveDataObject() {
377        RuleManager ruleWrapper = (RuleManager) getDataObject();
378
379        for (AgendaEditor agenda : ruleWrapper.getAgendas()) {
380
381            //Check if this agenda has anything to save
382            if (agenda.isDummyAgenda()) {
383                continue;
384            }
385
386            //Set the agenda name.
387            agenda.setName(ruleWrapper.getRefObjectId() + ":" + agenda.getAgendaTypeInfo().getId() + ":1");
388
389            //Retrieve the context and set the id on the agenda.
390            if (agenda.getContextId() == null) {
391                ContextDefinition context = this.getRuleManagementService().getContextByNameAndNamespace("Course Requirements", ruleWrapper.getNamespace());
392                agenda.setContextId(context.getId());
393            }
394
395            //Create or update the agenda.
396            if (agenda.getId() == null) {
397
398                //Check if someone else has not created an agenda while this one was created.
399                if(this.getRuleManagementService().getAgendaByNameAndContextId(agenda.getName(), agenda.getContextId())!=null){
400                    throw new KRMSOptimisticLockingException();
401                }
402
403                AgendaDefinition.Builder agendaBldr = AgendaDefinition.Builder.create(agenda);
404                AgendaDefinition agendaDfn = this.getRuleManagementService().createAgenda(agendaBldr.build());
405
406                //Set the id and versionnumber for a possible update.
407                agenda.setId(agendaDfn.getId());
408                agenda.setVersionNumber(agendaDfn.getVersionNumber());
409                agenda.setFirstItemId(agendaDfn.getFirstItemId());
410
411                //Create the reference object binding only on create agenda, no need to update.
412                ReferenceObjectBinding.Builder refBuilder = ReferenceObjectBinding.Builder.create("Agenda",
413                        agendaDfn.getId(), ruleWrapper.getNamespace(), ruleWrapper.getRefDiscriminatorType(), ruleWrapper.getRefObjectId());
414                this.getRuleManagementService().createReferenceObjectBinding(refBuilder.build());
415            }
416
417            //Update the root item.
418            try {
419
420                //Set the first agenda item id and save the agenda items
421                AgendaItemDefinition firstItem = maintainAgendaItems(agenda, ruleWrapper.getRefObjectId() + ":", ruleWrapper.getNamespace());
422
423                //If no more rules linked to agenda, delete it.
424                if (firstItem.getRule() == null) {
425                    List<ReferenceObjectBinding> refObjectsBindings = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(ruleWrapper.getRefDiscriminatorType(), ruleWrapper.getRefObjectId());
426                    for (ReferenceObjectBinding referenceObjectBinding : refObjectsBindings) {
427                        if (referenceObjectBinding.getKrmsObjectId().equals(agenda.getId())) {
428                            LOG.info("Deleting reference object binding for id: {}", referenceObjectBinding.getId());
429                            this.getRuleManagementService().deleteReferenceObjectBinding(referenceObjectBinding.getId());
430                        }
431                    }
432                    this.getRuleManagementService().deleteAgenda(agenda.getId());
433                }
434
435
436            } catch (OjbOperationException e) {
437                //OptimisticLockException
438                if (e.getCause() instanceof OptimisticLockException) {
439                    throw new KRMSOptimisticLockingException("RuleEditorMaintainableImpl Optimistic Lock exception",e);
440                } else {
441                    throw e;
442                }
443            }
444
445        }
446
447    }
448
449    public AgendaItemDefinition maintainAgendaItems(AgendaEditor agenda, String namePrefix, String nameSpace) {
450
451        Queue<RuleDefinition.Builder> rules = new LinkedList<RuleDefinition.Builder>();
452        for (RuleEditor rule : agenda.getRuleEditors().values()) {
453            if (!rule.isDummy()) {
454                rules.add(this.finRule(rule, namePrefix, nameSpace));
455            }
456        }
457
458        AgendaItemDefinition.Builder rootItemBuilder = manageFirstItem(agenda);
459
460        AgendaItemDefinition.Builder itemToDelete = null;
461        AgendaItemDefinition.Builder itemBuilder = rootItemBuilder;
462        while (rules.peek() != null) {
463            itemBuilder.setRule(rules.poll());
464            itemBuilder.setRuleId(itemBuilder.getRule().getId());
465            if (rules.peek() != null) {
466                if(itemBuilder.getWhenTrue()==null){
467                    itemBuilder.setWhenTrue(AgendaItemDefinition.Builder.create(null, agenda.getId()));
468                }
469                itemBuilder = itemBuilder.getWhenTrue();
470            } else {
471                itemToDelete = itemBuilder.getWhenTrue();
472                itemBuilder.setWhenTrue(null);
473            }
474        }
475
476        return manageAgendaItems(agenda, rootItemBuilder, itemToDelete);
477    }
478
479    protected AgendaItemDefinition.Builder manageFirstItem(AgendaEditor agenda) {
480        AgendaItemDefinition.Builder rootItemBuilder;
481        if(agenda.getFirstItemId()!=null) {
482            AgendaItemDefinition firstItem = this.getRuleManagementService().getAgendaItem(agenda.getFirstItemId());
483            rootItemBuilder = AgendaItemDefinition.Builder.create(firstItem);
484            rootItemBuilder.setRule(null);
485            rootItemBuilder.setRuleId(null);
486        } else {
487            rootItemBuilder = AgendaItemDefinition.Builder.create(null, agenda.getId());
488        }
489        return rootItemBuilder;
490    }
491
492    protected AgendaItemDefinition manageAgendaItems(AgendaEditor agenda, AgendaItemDefinition.Builder rootItemBuilder, AgendaItemDefinition.Builder itemToDelete) {
493        //Update the root item.
494        AgendaItemDefinition agendaItem = rootItemBuilder.build();
495        try {
496            if(agendaItem.getId()==null){
497                agendaItem = this.getRuleManagementService().createAgendaItem(agendaItem);
498                agenda.setFirstItemId(agendaItem.getId());
499                AgendaDefinition.Builder agendaBldr = AgendaDefinition.Builder.create(agenda);
500                this.getRuleManagementService().updateAgenda(agendaBldr.build());
501            } else {
502                this.getRuleManagementService().updateAgendaItem(agendaItem);
503            }
504
505            //delete agendaitems not used.
506            if(itemToDelete!=null){
507                this.deleteAgendaItems(itemToDelete.build());
508            }
509        } catch (OjbOperationException e) {
510            //OptimisticLockException
511            if (e.getCause() instanceof OptimisticLockException) {
512                throw new KRMSOptimisticLockingException("Could not obtain OjbOperation ",e);
513            } else {
514                throw e;
515            }
516        }
517
518        //Delete orhpan rules
519        for (RuleEditor deletedRule : agenda.getDeletedRules()) {
520            this.getRuleManagementService().deleteRule(deletedRule.getId());
521        }
522        return agendaItem;
523    }
524
525    public void deleteAgendaItems(AgendaItemDefinition agendaItem) {
526        if (agendaItem != null) {
527            //Update the agenda (hack so that it does not delete the rule.)
528            AgendaItemDefinition.Builder itemBuilder = AgendaItemDefinition.Builder.create(agendaItem.getId(), agendaItem.getAgendaId());
529            itemBuilder.setVersionNumber(agendaItem.getVersionNumber());
530            this.getRuleManagementService().updateAgendaItem(itemBuilder.build());
531
532            //Delete the agenda item
533            this.getRuleManagementService().deleteAgendaItem(agendaItem.getId());
534            deleteAgendaItems(agendaItem.getWhenFalse());
535            deleteAgendaItems(agendaItem.getWhenTrue());
536            deleteAgendaItems(agendaItem.getAlways());
537        }
538    }
539
540    public RuleDefinition.Builder finRule(RuleEditor rule, String rulePrefix, String namespace) {
541        // handle saving new parameterized terms
542        if (rule.getPropositionEditor() != null) {
543            this.onSubmit(rule.getPropositionEditor());
544        }
545
546        if (rule.getNamespace() == null) {
547            rule.setNamespace(namespace);
548        }
549        rule.setName(rulePrefix + rule.getRuleTypeInfo().getId() + ":1");
550
551        //Check if someone else has not created a rule while this one was created.
552        if(rule.getId()==null){
553            if(this.getRuleManagementService().getRuleByNameAndNamespace(rule.getName(), rule.getNamespace())!=null){
554                throw new KRMSOptimisticLockingException();
555            }
556        }
557
558        return RuleDefinition.Builder.create(rule);
559    }
560
561    public void onSubmit(PropositionEditor propositionEditor) {
562        if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
563
564            //Call onsubmit on the associated builder.
565            ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType());
566            if (builder != null) {
567                builder.onSubmit(propositionEditor);
568            }
569
570        } else {
571
572            //If not a simple node, recursively finalize the child proposition editors.
573            for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
574                onSubmit(child);
575            }
576
577        }
578    }
579
580    public void initPropositionEditor(PropositionEditor propositionEditor) {
581        if (propositionEditor == null) {
582            return;
583        }
584
585        if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
586
587            if (propositionEditor.getType() == null) {
588                KrmsTypeDefinition type = this.getKrmsTypeRepositoryService().getTypeById(propositionEditor.getTypeId());
589                propositionEditor.setType(type.getName());
590            }
591
592            Map<String, String> termParameters = this.getTermParameters(propositionEditor);
593            ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType());
594            if (builder != null) {
595                builder.resolveTermParameters(propositionEditor, termParameters);
596            }
597        } else {
598            for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
599                initPropositionEditor(child);
600            }
601
602        }
603    }
604
605    public Map<String, String> getTermParameters(PropositionEditor proposition) {
606
607        Map<String, String> termParameters = new HashMap<String, String>();
608        if (proposition.getTerm() == null) {
609            PropositionParameterEditor termParameter = PropositionTreeUtil.getTermParameter(proposition.getParameters());
610            if (termParameter != null) {
611
612                if (termParameter.getTermValue() == null) {
613                    proposition.setTerm(new TermEditor());
614                } else {
615                    proposition.setTerm(new TermEditor(termParameter.getTermValue()));
616                }
617
618            } else {
619                return termParameters;
620            }
621        }
622
623        for (TermParameterEditor parameter : proposition.getTerm().getEditorParameters()) {
624            termParameters.put(parameter.getName(), parameter.getValue());
625        }
626
627        return termParameters;
628    }
629
630    /**
631     * In the case of edit maintenance adds a new blank line to the old side
632     * <p/>
633     *
634     * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.ViewModel, Object, String, String, boolean)
635     */
636    @Override
637    public void processAfterAddLine(ViewModel viewModel, Object addLine, String collectionId, String collectionPath, boolean isValidLine) {
638        // Check for maintenance documents in edit but exclude notes
639        if (viewModel instanceof MaintenanceDocumentForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm) viewModel).getMaintenanceAction()) && !(addLine instanceof Note)) {
640
641            // Figure out which rule is being edited
642            RuleEditor rule = getRuleEditor(viewModel);
643            // Figure out which proposition is being edited
644            Tree<RuleEditorTreeNode, String> propositionTree = rule.getEditTree();
645            Node<RuleEditorTreeNode, String> editedPropositionNode = PropositionTreeUtil.findEditedProposition(propositionTree.getRootElement());
646
647            BindingInfo bindingInfo = (BindingInfo) viewModel.getViewPostMetadata().getComponentPostData(collectionId,
648                    UifConstants.PostMetadata.BINDING_INFO);
649            // get the old object's collection
650            Collection<Object> oldCollection = ObjectPropertyUtils
651                    .getPropertyValue(editedPropositionNode.getData(),
652                            bindingInfo.getBindingName());
653
654            Class<?> collectionObjectClass = (Class<?>) viewModel.getViewPostMetadata().getComponentPostData(collectionId,
655                    UifConstants.PostMetadata.COLL_OBJECT_CLASS);
656            try {
657                Object blankLine = collectionObjectClass.newInstance();
658                //Add a blank line to the top of the collection
659                if (oldCollection instanceof List) {
660                    ((List) oldCollection).add(0, blankLine);
661                } else {
662                    oldCollection.add(blankLine);
663                }
664            } catch (Exception e) {
665                throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
666            }
667        }
668    }
669
670    public RuleManagementService getRuleManagementService() {
671        if (ruleManagementService == null) {
672            ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "ruleManagementService"));
673        }
674        return ruleManagementService;
675    }
676
677    public KrmsTypeRepositoryService getKrmsTypeRepositoryService() {
678        if (krmsTypeRepositoryService == null) {
679            krmsTypeRepositoryService = (KrmsTypeRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "krmsTypeRepositoryService"));
680        }
681        return krmsTypeRepositoryService;
682    }
683
684    public TermRepositoryService getTermRepositoryService() {
685        if (termRepositoryService == null) {
686            termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService"));
687        }
688        return termRepositoryService;
689    }
690
691    private TemplateRegistry getTemplateRegistry() {
692        if (templateRegistry == null) {
693            templateRegistry = (TemplateRegistry) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/templateResolverService", "templateResolverService"));
694        }
695        return templateRegistry;
696    }
697}