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     */
016    package org.kuali.rice.krms.service.impl;
017    
018    import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
019    import org.kuali.rice.core.api.util.tree.Node;
020    import org.kuali.rice.core.api.util.tree.Tree;
021    import org.kuali.rice.krad.bo.Note;
022    import org.kuali.rice.krad.maintenance.MaintenanceDocument;
023    import org.kuali.rice.krad.uif.container.CollectionGroup;
024    import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
025    import org.kuali.rice.krad.uif.view.View;
026    import org.kuali.rice.krad.util.KRADConstants;
027    import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
028    import org.kuali.rice.krms.api.KrmsConstants;
029    import org.kuali.rice.krms.api.repository.RuleManagementService;
030    import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
031    import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
032    import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
033    import org.kuali.rice.krms.api.repository.agenda.AgendaTreeEntryDefinitionContract;
034    import org.kuali.rice.krms.api.repository.agenda.AgendaTreeRuleEntry;
035    import org.kuali.rice.krms.api.repository.proposition.PropositionType;
036    import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
037    import org.kuali.rice.krms.api.repository.term.TermDefinition;
038    import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
039    import org.kuali.rice.krms.api.repository.term.TermResolverDefinition;
040    import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition;
041    import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
042    import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
043    import org.kuali.rice.krms.dto.AgendaEditor;
044    import org.kuali.rice.krms.dto.PropositionEditor;
045    import org.kuali.rice.krms.dto.PropositionParameterEditor;
046    import org.kuali.rice.krms.dto.RuleEditor;
047    import org.kuali.rice.krms.dto.RuleManagementWrapper;
048    import org.kuali.rice.krms.dto.TemplateInfo;
049    import org.kuali.rice.krms.dto.TermEditor;
050    import org.kuali.rice.krms.dto.TermParameterEditor;
051    import org.kuali.rice.krms.builder.ComponentBuilder;
052    import org.kuali.rice.krms.service.TemplateRegistry;
053    import org.kuali.rice.krms.tree.RuleCompareTreeBuilder;
054    import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
055    import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
056    import org.kuali.rice.krms.service.RuleEditorMaintainable;
057    import org.kuali.student.enrollment.class1.krms.dto.EnrolRuleManagementWrapper;
058    import org.kuali.student.enrollment.class2.courseoffering.service.decorators.PermissionServiceConstants;
059    import org.kuali.rice.krms.util.PropositionTreeUtil;
060    import org.kuali.student.enrollment.uif.service.impl.KSMaintainableImpl;
061    import org.kuali.student.r2.common.dto.ContextInfo;
062    
063    import javax.xml.namespace.QName;
064    import java.util.ArrayList;
065    import java.util.Collection;
066    import java.util.HashMap;
067    import java.util.List;
068    import 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     */
075    public 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    }