001/**
002 * Copyright 2005-2012 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.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
019import org.kuali.rice.core.api.util.tree.Node;
020import org.kuali.rice.core.api.util.tree.Tree;
021import org.kuali.rice.krad.bo.Note;
022import org.kuali.rice.krad.maintenance.MaintenanceDocument;
023import org.kuali.rice.krad.uif.container.CollectionGroup;
024import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
025import org.kuali.rice.krad.uif.view.View;
026import org.kuali.rice.krad.util.KRADConstants;
027import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
028import org.kuali.rice.krms.api.KrmsConstants;
029import org.kuali.rice.krms.api.repository.RuleManagementService;
030import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
031import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
032import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
033import org.kuali.rice.krms.api.repository.agenda.AgendaTreeEntryDefinitionContract;
034import org.kuali.rice.krms.api.repository.agenda.AgendaTreeRuleEntry;
035import org.kuali.rice.krms.api.repository.proposition.PropositionType;
036import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
037import org.kuali.rice.krms.api.repository.term.TermDefinition;
038import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
039import org.kuali.rice.krms.api.repository.term.TermResolverDefinition;
040import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition;
041import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
042import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
043import org.kuali.rice.krms.dto.AgendaEditor;
044import org.kuali.rice.krms.dto.PropositionEditor;
045import org.kuali.rice.krms.dto.PropositionParameterEditor;
046import org.kuali.rice.krms.dto.RuleEditor;
047import org.kuali.rice.krms.dto.RuleManagementWrapper;
048import org.kuali.rice.krms.dto.TemplateInfo;
049import org.kuali.rice.krms.dto.TermEditor;
050import org.kuali.rice.krms.dto.TermParameterEditor;
051import org.kuali.rice.krms.builder.ComponentBuilder;
052import org.kuali.rice.krms.service.TemplateRegistry;
053import org.kuali.rice.krms.tree.RuleCompareTreeBuilder;
054import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
055import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
056import org.kuali.rice.krms.service.RuleEditorMaintainable;
057import org.kuali.student.enrollment.class1.krms.dto.EnrolRuleManagementWrapper;
058import org.kuali.student.enrollment.class2.courseoffering.service.decorators.PermissionServiceConstants;
059import org.kuali.rice.krms.util.PropositionTreeUtil;
060import org.kuali.student.enrollment.uif.service.impl.KSMaintainableImpl;
061import org.kuali.student.r2.common.dto.ContextInfo;
062
063import javax.xml.namespace.QName;
064import java.util.ArrayList;
065import java.util.Collection;
066import java.util.HashMap;
067import java.util.List;
068import java.util.Map;
069
070/**
071 * {@link org.kuali.rice.krad.maintenance.Maintainable} for the {@link org.kuali.rice.krms.impl.ui.AgendaEditor}
072 *
073 * @author Kuali Rice Team (rice.collab@kuali.org)
074 */
075public class RuleEditorMaintainableImpl extends KSMaintainableImpl implements RuleEditorMaintainable {
076
077    private static final long serialVersionUID = 1L;
078
079    private transient RuleManagementService ruleManagementService;
080    private transient KrmsTypeRepositoryService krmsTypeRepositoryService;
081    private transient TermRepositoryService termRepositoryService;
082
083    private transient ContextInfo contextInfo;
084    private transient TemplateRegistry templateRegistry;
085
086    public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document";
087
088    /**
089     * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject
090     *
091     * @param model the MaintenanceDocumentForm
092     * @return the AgendaEditor
093     */
094    private RuleEditor getRuleEditor(Object model) {
095        MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
096        return (RuleEditor) maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject();
097    }
098
099    @Override
100    public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
101        EnrolRuleManagementWrapper dataObject = new EnrolRuleManagementWrapper();
102
103        List<AgendaEditor> agendas = new ArrayList<AgendaEditor>();
104        dataObject.setAgendas(agendas);
105
106        String coId = dataObjectKeys.get("refObjectId");
107        dataObject.setRefObjectId(coId);
108
109        dataObject.setCompareTree(RuleCompareTreeBuilder.initCompareTree());
110
111        return dataObject;
112    }
113
114    protected AgendaEditor getAgendaEditor(String agendaId) {
115        AgendaDefinition agenda = this.getRuleManagementService().getAgenda(agendaId);
116        AgendaEditor agendaEditor = new AgendaEditor(agenda);
117
118        AgendaTreeDefinition agendaTree = this.getRuleManagementService().getAgendaTree(agendaId);
119        agendaEditor.setRuleEditors(getRuleEditorsFromTree(agendaTree.getEntries()));
120
121        return agendaEditor;
122    }
123
124    protected List<RuleEditor> getRuleEditorsFromTree(List<AgendaTreeEntryDefinitionContract> agendaTreeEntries) {
125
126        RuleViewTreeBuilder viewTreeBuilder = new RuleViewTreeBuilder();
127        viewTreeBuilder.setRuleManagementService(this.getRuleManagementService());
128        List<RuleEditor> rules = new ArrayList<RuleEditor>();
129        for (AgendaTreeEntryDefinitionContract treeEntry : agendaTreeEntries) {
130            if (treeEntry instanceof AgendaTreeRuleEntry) {
131                AgendaTreeRuleEntry treeRuleEntry = (AgendaTreeRuleEntry) treeEntry;
132                AgendaItemDefinition agendaItem = this.getRuleManagementService().getAgendaItem(treeEntry.getAgendaItemId());
133
134                if (agendaItem.getRuleId() != null) {
135                    RuleDefinition rule = this.getRuleManagementService().getRule(treeRuleEntry.getRuleId());
136                    RuleEditor ruleEditor = new RuleEditor(rule);
137                    this.initPropositionEditor((PropositionEditor) ruleEditor.getProposition());
138                    ruleEditor.setViewTree(viewTreeBuilder.buildTree(ruleEditor));
139                    rules.add(ruleEditor);
140                }
141
142                if (treeRuleEntry.getIfTrue() != null) {
143                    rules.addAll(getRuleEditorsFromTree(treeRuleEntry.getIfTrue().getEntries()));
144                }
145            }
146        }
147        return rules;
148    }
149
150    /**
151     * {@inheritDoc}
152     */
153    @Override
154    public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
155        super.processAfterNew(document, requestParameters);
156        document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
157    }
158
159    @Override
160    public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
161        super.processAfterEdit(document, requestParameters);
162        document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document");
163    }
164
165    @Override
166    public void saveDataObject() {
167        RuleManagementWrapper ruleWrapper = (RuleManagementWrapper) getDataObject();
168
169        for (AgendaEditor agenda : ruleWrapper.getAgendas()){
170
171            List<RuleEditor> deleteRuleList = new ArrayList<RuleEditor>();
172            for(RuleEditor rule : agenda.getRuleEditors()) {
173                if(!rule.isDummy()) {
174                    this.finRule(rule, ruleWrapper.getNamePrefix(), ruleWrapper.getNamespace());
175                } else {
176                    deleteRuleList.add(rule);
177                }
178            }
179            agenda.getRuleEditors().removeAll(deleteRuleList);
180
181            AgendaDefinition.Builder agendaBuilder = null;
182            AgendaDefinition agendaDefinition = null;
183            if(agenda.getId() == null) {
184                agendaBuilder = AgendaDefinition.Builder.create(agenda);
185                agendaDefinition = agendaBuilder.build();
186            }
187
188            if(agenda.getRuleEditors().size() == 0) {
189                for(String deletedRuleId : ruleWrapper.getDeletedRuleIds()) {
190                    this.getRuleManagementService().deleteAgendaItem(deletedRuleId);
191                }
192                this.getRuleManagementService().deleteReferenceObjectBinding(agenda.getId());
193                this.getRuleManagementService().deleteAgenda(agenda.getId());
194            } else {
195                if(agendaDefinition == null) {
196                    agendaBuilder = AgendaDefinition.Builder.create(agenda);
197                    agendaDefinition = agendaBuilder.build();
198                }
199                this.getRuleManagementService().updateAgenda(agendaDefinition);
200            }
201        }
202
203    }
204
205    protected void finRule(RuleEditor rule, String rulePrefix, String namespace) {
206        // handle saving new parameterized terms
207        PropositionEditor proposition = (PropositionEditor) rule.getProposition();
208        if (proposition != null) {
209            this.finPropositionEditor(proposition);
210        }
211
212        if(rule.getName() == null) {
213            rule.setName(rulePrefix + " " + rule.getDescription());
214        }
215        if(rule.getNamespace() == null) {
216            rule.setNamespace(namespace);
217        }
218
219        RuleDefinition.Builder ruleBuilder = RuleDefinition.Builder.create(rule);
220        RuleDefinition ruleDefinition = ruleBuilder.build();
221        if (ruleDefinition.getId() == null) {
222            this.getRuleManagementService().createRule(ruleDefinition);
223        } else {
224            this.getRuleManagementService().updateRule(ruleDefinition);
225        }
226    }
227
228    protected void finPropositionEditor(PropositionEditor propositionEditor) {
229        if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
230
231            //Save term and set termid.
232            String termId = this.saveTerm(propositionEditor);
233            if (propositionEditor.getParameters().get(0) != null) {
234                propositionEditor.getParameters().get(0).setValue(termId);
235            }
236
237            //Set the default operation and value
238            TemplateInfo template = this.getTemplateRegistry().getTemplateForType(propositionEditor.getType());
239            propositionEditor.getParameters().get(2).setValue(template.getOperator());
240
241            if (!"n".equals(template.getValue())) {
242                propositionEditor.getParameters().get(1).setValue(template.getValue());
243            }
244
245        } else {
246
247            //If not a simple node, recursively finalize the child proposition editors.
248            for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
249                finPropositionEditor(child);
250            }
251
252        }
253    }
254
255    protected String saveTerm(PropositionEditor propositionEditor) {
256
257        //Set the termSpecification based on current type.
258        TermEditor term = propositionEditor.getTerm();
259        term.setSpecification(this.getTermSpecForType(propositionEditor.getType()));
260
261        TermDefinition.Builder termBuilder = TermDefinition.Builder.create(term);
262        TermDefinition termDefinition = termBuilder.build();
263        if (term.getId() == null) {
264            termDefinition = this.getTermRepositoryService().createTerm(termDefinition);
265
266        } else {
267            this.getTermRepositoryService().updateTerm(termDefinition);
268        }
269
270        return termDefinition.getId();
271    }
272
273    protected TermSpecificationDefinition getTermSpecForType(String type) {
274
275        //Get the term output name for this type.
276        String termSpecName = this.getTemplateRegistry().getTermSpecNameForType(type);
277
278        List<TermResolverDefinition> matchingTermResolvers = this.getTermRepositoryService().findTermResolversByNamespace(PermissionServiceConstants.KS_SYS_NAMESPACE);
279        for (TermResolverDefinition termResolver : matchingTermResolvers) {
280            TermSpecificationDefinition termSpec = termResolver.getOutput();
281            if (termSpec.getName().equals(termSpecName)) {
282                return termSpec;
283            }
284        }
285
286        return null;
287    }
288
289    protected void initPropositionEditor(PropositionEditor propositionEditor) {
290        if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) {
291
292            if (propositionEditor.getType() == null) {
293                KrmsTypeDefinition type = this.getKrmsTypeRepositoryService().getTypeById(propositionEditor.getTypeId());
294                propositionEditor.setType(type.getName());
295            }
296
297            Map<String, String> termParameters = this.getTermParameters(propositionEditor);
298            ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType());
299            if (builder != null) {
300                builder.resolveTermParameters(propositionEditor, termParameters);
301            }
302        } else {
303            for (PropositionEditor child : propositionEditor.getCompoundEditors()) {
304                initPropositionEditor(child);
305            }
306
307        }
308    }
309
310    protected Map<String, String> getTermParameters(PropositionEditor proposition) {
311
312        Map<String, String> termParameters = new HashMap<String, String>();
313        if (proposition.getTerm() == null) {
314            if (proposition.getParameters().get(0) != null) {
315
316                PropositionParameterEditor termParameter = proposition.getParameters().get(0);
317                if (termParameter.getTermValue() == null){
318                    String termId = proposition.getParameters().get(0).getValue();
319                    termParameter.setTermValue(this.getTermRepositoryService().getTerm(termId));
320                }
321                proposition.setTerm(new TermEditor(termParameter.getTermValue()));
322            } else {
323                return termParameters;
324            }
325        }
326
327        for (TermParameterEditor parameter : proposition.getTerm().getEditorParameters()) {
328            termParameters.put(parameter.getName(), parameter.getValue());
329        }
330
331        return termParameters;
332    }
333
334    /**
335     * In the case of edit maintenance adds a new blank line to the old side
336     * <p/>
337     * TODO: should this write some sort of missing message on the old side
338     * instead?
339     *
340     * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View,
341     *      org.kuali.rice.krad.uif.container.CollectionGroup, Object,
342     *      Object)
343     */
344    @Override
345    protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
346        // Check for maintenance documents in edit but exclude notes
347        if (model instanceof MaintenanceDocumentForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm) model).getMaintenanceAction()) && !(addLine instanceof Note)) {
348            MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
349            MaintenanceDocument document = maintenanceDocumentForm.getDocument();
350
351            // Figure out which rule is being edited
352            RuleEditor rule = getRuleEditor(model);
353            // Figure out which proposition is being edited
354            Tree<RuleEditorTreeNode, String> propositionTree = rule.getEditTree();
355            Node<RuleEditorTreeNode, String> editedPropositionNode = PropositionTreeUtil.findEditedProposition(propositionTree.getRootElement());
356
357            // get the old object's collection
358            Collection<Object> oldCollection = ObjectPropertyUtils
359                    .getPropertyValue(editedPropositionNode.getData(),
360                            collectionGroup.getPropertyName());
361
362            try {
363                Object blankLine = collectionGroup.getCollectionObjectClass().newInstance();
364                //Add a blank line to the top of the collection
365                if (oldCollection instanceof List) {
366                    ((List) oldCollection).add(0, blankLine);
367                } else {
368                    oldCollection.add(blankLine);
369                }
370            } catch (Exception e) {
371                throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
372            }
373        }
374    }
375
376    public RuleManagementService getRuleManagementService() {
377        if (ruleManagementService == null) {
378            ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "ruleManagementService"));
379        }
380        return ruleManagementService;
381    }
382
383    public KrmsTypeRepositoryService getKrmsTypeRepositoryService() {
384        if (krmsTypeRepositoryService == null) {
385            krmsTypeRepositoryService = (KrmsTypeRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "krmsTypeRepositoryService"));
386        }
387        return krmsTypeRepositoryService;
388    }
389
390    public TermRepositoryService getTermRepositoryService() {
391        if (termRepositoryService == null) {
392            termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService"));
393        }
394        return termRepositoryService;
395    }
396
397    private TemplateRegistry getTemplateRegistry() {
398        if (templateRegistry == null) {
399            templateRegistry = (TemplateRegistry) GlobalResourceLoader.getService(QName.valueOf("templateResolverMockService"));
400        }
401        return templateRegistry;
402    }
403}