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