View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
20  import org.kuali.rice.core.api.util.tree.Tree;
21  import org.kuali.rice.krad.uif.UifConstants;
22  import org.kuali.rice.krad.uif.component.Component;
23  import org.kuali.rice.krad.uif.container.Container;
24  import org.kuali.rice.krad.uif.util.ComponentFactory;
25  import org.kuali.rice.krad.uif.util.ComponentUtils;
26  import org.kuali.rice.krad.uif.view.View;
27  import org.kuali.rice.krad.util.BeanPropertyComparator;
28  import org.kuali.rice.krad.util.ObjectUtils;
29  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
30  import org.kuali.rice.krms.api.KrmsConstants;
31  import org.kuali.rice.krms.api.repository.LogicalOperator;
32  import org.kuali.rice.krms.api.repository.RuleManagementService;
33  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
34  import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
35  import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
36  import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
37  import org.kuali.rice.krms.builder.ComponentBuilder;
38  import org.kuali.rice.krms.dto.PropositionEditor;
39  import org.kuali.rice.krms.dto.PropositionParameterEditor;
40  import org.kuali.rice.krms.dto.RuleEditor;
41  import org.kuali.rice.krms.dto.RuleManagementWrapper;
42  import org.kuali.rice.krms.dto.TermEditor;
43  import org.kuali.rice.krms.dto.TermParameterEditor;
44  import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
45  import org.kuali.rice.krms.service.TemplateRegistry;
46  import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
47  import org.kuali.rice.krms.tree.node.CompareTreeNode;
48  import org.kuali.rice.krms.tree.RuleCompareTreeBuilder;
49  import org.kuali.rice.krms.tree.RuleEditTreeBuilder;
50  import org.kuali.rice.krms.tree.RulePreviewTreeBuilder;
51  import org.kuali.rice.krms.util.KRMSConstants;
52  import org.kuali.rice.krms.util.NaturalLanguageHelper;
53  import org.kuali.rice.krms.util.PropositionTreeUtil;
54  import org.kuali.rice.krms.dto.TemplateInfo;
55  import org.kuali.rice.krms.service.RuleViewHelperService;
56  import org.kuali.student.common.uif.service.impl.KSViewHelperServiceImpl;
57  import org.kuali.student.r1.common.rice.StudentIdentityConstants;
58  import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
59  import org.springframework.beans.BeanUtils;
60  
61  import javax.xml.namespace.QName;
62  import java.util.ArrayList;
63  import java.util.Arrays;
64  import java.util.List;
65  import java.util.Map;
66  
67  /**
68   * Helpers Service for the Rule Pages.
69   *
70   * @author Kuali Student Team
71   */
72  public class RuleViewHelperServiceImpl extends KSViewHelperServiceImpl implements RuleViewHelperService {
73  
74      private transient RuleManagementService ruleManagementService;
75      private transient KrmsTypeRepositoryService krmsTypeRepositoryService;
76      private transient TermRepositoryService termRepositoryService;
77  
78      private RuleCompareTreeBuilder compareTreeBuilder;
79      private RuleEditTreeBuilder editTreeBuilder;
80      private RulePreviewTreeBuilder previewTreeBuilder;
81      private RuleViewTreeBuilder viewTreeBuilder;
82  
83      private NaturalLanguageHelper naturalLanguageHelper;
84  
85      private static TemplateRegistry templateRegistry;
86  
87      protected RuleEditor getRuleEditor(Object model) {
88          if (model instanceof MaintenanceDocumentForm) {
89              MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
90              Object dataObject = maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject();
91  
92              if (dataObject instanceof RuleEditor) {
93                  return (RuleEditor) dataObject;
94              } else if (dataObject instanceof RuleManagementWrapper) {
95                  RuleManagementWrapper wrapper = (RuleManagementWrapper) dataObject;
96                  return wrapper.getRuleEditor();
97              }
98          }
99          return null;
100     }
101 
102     @Override
103     public TemplateInfo getTemplateForType(String type) {
104         return this.getTemplateRegistry().getTemplateForType(type);
105     }
106 
107     @Override
108     protected void addCustomContainerComponents(View view, Object model, Container container) {
109         if (KRMSConstants.KRMS_PROPOSITION_DETAILSECTION_ID.equals(container.getId())) {
110             customizePropositionEditSection(view, model, container);
111         }
112     }
113 
114     private void customizePropositionEditSection(View view, Object model, Container container) {
115         //Retrieve the current editing proposition if exists.
116         MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model;
117         Object dataObject = maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject();
118 
119         RuleEditor ruleEditor = ((RuleManagementWrapper) dataObject).getRuleEditor();
120         PropositionEditor propEditor = PropositionTreeUtil.getProposition(ruleEditor);
121 
122         List<Component> components = new ArrayList<Component>();
123         if (propEditor != null) {
124             //Retrieve the name of the xml component to display for the proposition type.
125             TemplateInfo template = this.getTemplateForType(propEditor.getType());
126 
127             if (template != null && template.getComponentId() != null) {
128                 Component component = ComponentFactory.getNewComponentInstance(template.getComponentId());
129                 view.assignComponentIds(component);
130                 if(container.getId().equals(maintenanceDocumentForm.getUpdateComponentId())){
131                     String nodePath = view.getDefaultBindingObjectPath() + "." + propEditor.getBindingPath();
132                     ComponentUtils.pushObjectToContext(component, UifConstants.ContextVariableNames.NODE_PATH, nodePath);
133                     ComponentUtils.prefixBindingPathNested(component, propEditor.getBindingPath());
134                 }
135 
136                 //Add Proposition Type FieldGroup to Tree Node
137                 components.add(component);
138             }
139 
140             if (template != null && template.getConstantComponentId() != null) {
141                 Component component = ComponentFactory.getNewComponentInstance(template.getConstantComponentId());
142                 view.assignComponentIds(component);
143 
144                 //Add Proposition Type FieldGroup to Tree Node
145                 components.add(component);
146             }
147         }
148 
149         //Do not display if there are no components.
150         if (components.size() == 0) {
151             container.getHeader().setRender(false);
152         }
153 
154         container.setItems(components);
155     }
156 
157     /**
158      * Validate the proposition.
159      *
160      * @param proposition
161      */
162     @Override
163     public void validateProposition(PropositionEditor proposition) {
164 
165         // Retrieve the builder for the current proposition type.
166         ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(proposition.getType());
167         if (builder != null) {
168             // Execute validation
169             builder.validate(proposition);
170         }
171     }
172 
173     /**
174      * Clear the description and natural language on proposition editors.
175      *
176      * @param prop
177      * @return
178      */
179     @Override
180     public void resetDescription(PropositionEditor prop) {
181 
182         //If proposition type is null, set description and term null
183         if (prop.getType() == null) {
184             prop.setDescription(StringUtils.EMPTY);
185             prop.setTerm(null);
186             prop.getNaturalLanguage().clear();
187             return;
188         }
189 
190         //Build the new termParamters with the matching component builder.
191         if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(prop.getPropositionTypeCode())) {
192             Map<String, String> termParameters = null;
193             ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(prop.getType());
194             if (builder != null) {
195                 termParameters = builder.buildTermParameters(prop);
196             }
197 
198             List<TermParameterEditor> parameters = new ArrayList<TermParameterEditor>();
199             if (termParameters != null) {
200                 for (Map.Entry<String, String> entry : termParameters.entrySet()) {
201 
202                     TermParameterEditor parameterEditor = null;
203                     if (prop.getTerm().getParameters() != null) {
204                         for (TermParameterEditor parameter : prop.getTerm().getEditorParameters()) {
205 
206                             if (entry.getKey().equals(parameter.getName())) {
207                                 parameterEditor = parameter;
208                                 parameterEditor.setValue(entry.getValue());
209                                 break;
210                             }
211                         }
212                     }
213 
214                     //Create a new parameter if not exist.
215                     if (parameterEditor == null) {
216                         parameterEditor = new TermParameterEditor();
217                         parameterEditor.setName(entry.getKey());
218                         parameterEditor.setValue(entry.getValue());
219                     }
220                     parameters.add(parameterEditor);
221                 }
222             }
223 
224             prop.getTerm().setParameters(parameters);
225 
226             //Set the term specification if it doesn't exist.
227             if (prop.getTerm().getSpecification() == null) {
228                 String termSpecName = this.getTemplateRegistry().getTermSpecNameForType(prop.getType());
229                 prop.getTerm().setSpecification(getTermRepositoryService().getTermSpecificationByNameAndNamespace(termSpecName, KSKRMSServiceConstants.NAMESPACE_CODE));
230             }
231 
232         } else {
233             prop.setTerm(null);
234         }
235 
236         //Refresh the natural language.
237         prop.getNaturalLanguage().clear();
238     }
239 
240     public void configurePropositionForType(PropositionEditor proposition) {
241 
242         if (proposition != null) {
243 
244             if (PropositionType.COMPOUND.getCode().equalsIgnoreCase(proposition.getPropositionTypeCode())) {
245                 return;
246             }
247 
248             String propositionTypeId = proposition.getTypeId();
249             if ((propositionTypeId == null) || (propositionTypeId.isEmpty())) {
250                 proposition.setType(null);
251                 return;
252             }
253 
254             KrmsTypeDefinition type = KrmsRepositoryServiceLocator.getKrmsTypeRepositoryService().getTypeById(propositionTypeId);
255             if (type != null) {
256                 proposition.setType(type.getName());
257             }
258 
259             if (proposition.getTerm() == null) {
260                 proposition.setTerm(new TermEditor());
261             }
262 
263             String termSpecName = this.getTemplateRegistry().getTermSpecNameForType(proposition.getType());
264             proposition.getTerm().setSpecification(getTermRepositoryService().getTermSpecificationByNameAndNamespace(termSpecName, KSKRMSServiceConstants.NAMESPACE_CODE));
265 
266         }
267     }
268 
269     @Override
270     public void refreshInitTrees(RuleEditor rule) {
271 
272         if (rule == null) {
273             return;
274         }
275 
276         //Rebuild the trees
277         rule.setEditTree(this.getEditTreeBuilder().buildTree(rule));
278         rule.setPreviewTree(this.getPreviewTreeBuilder().buildTree(rule));
279 
280         //Also reset the logic expression. Should only be done after editTree is already built.
281         if (rule.getProposition() != null) {
282             rule.setLogicArea(PropositionTreeUtil.configureLogicExpression(rule.getPropositionEditor()));
283         } else {
284             rule.setLogicArea(StringUtils.EMPTY);
285         }
286 
287     }
288 
289     /**
290      * Rebuild the tree used for the view only trees.
291      *
292      * @param rule
293      */
294     @Override
295     public void refreshViewTree(RuleEditor rule) {
296 
297         if (rule == null) {
298             return;
299         }
300 
301         //Rebuild the trees
302         rule.setViewTree(this.getViewTreeBuilder().buildTree(rule));
303 
304     }
305 
306     @Override
307     public Tree<CompareTreeNode, String> buildCompareTree(RuleEditor original, RuleEditor compare) throws Exception {
308 
309         //Build the Tree
310         return this.getCompareTreeBuilder().buildTree(original, compare);
311 
312     }
313 
314     @Override
315     public Tree<CompareTreeNode, String> buildMultiViewTree(RuleEditor coRuleEditor, RuleEditor cluRuleEditor) throws Exception {
316 
317         //Build the Tree
318         return this.getCompareTreeBuilder().buildTree(coRuleEditor, cluRuleEditor);
319 
320     }
321 
322     /**
323      * Compare all the propositions in a rule tree with a parent rule tree. Returns false if any proposition's type
324      * or term parameters are not the same.
325      * <p/>
326      * Apart from the type and termparameters, all other detail is derived from the typeid and therefore not included in
327      * the comparison.     *
328      *
329      * @param original
330      * @return boolean
331      * @throws Exception
332      */
333     @Override
334     public Boolean compareRules(RuleEditor original) {
335 
336         //Do null check on propositions.
337         RuleEditor compareEditor = original.getParent();
338         if ((compareEditor == null) || (compareEditor.getProposition() == null)) {
339             if (original.getProposition() != null) {
340                 return false; //if compare is null and original is not, they differ.
341             } else {
342                 return true; //both of them are null.
343             }
344         } else if (original.getProposition() == null) {
345             return false;
346         }
347 
348         //Compare Root Proposition Type and if the same test recursively
349         if (original.getProposition().getTypeId().equals(compareEditor.getProposition().getTypeId())) {
350             return compareProposition(original.getPropositionEditor(), compareEditor.getPropositionEditor());
351         } else {
352             return false;
353         }
354     }
355 
356     /**
357      * Method to handle the proposition comparison recursively.
358      *
359      * @param original
360      * @param compare
361      * @return true if proposition are the same.
362      */
363     @Override
364     public Boolean compareProposition(PropositionEditor original, PropositionEditor compare) {
365         //Compare the proposition
366         BeanPropertyComparator propositionComparator = new BeanPropertyComparator(Arrays.asList("typeId"));
367         if (propositionComparator.compare(original, compare) != 0) {
368             return false;
369         }
370 
371         //Compare the term values
372         if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(original.getPropositionTypeCode())) {
373             TermEditor term = new TermEditor(PropositionTreeUtil.getTermParameter(compare.getParameters()).getTermValue());
374             if (!compareTerm(original.getTerm().getEditorParameters(), term.getEditorParameters())) {
375                 return false;
376             }
377         }
378 
379         //Compare the compound propositions.
380         return compareCompoundProposition(original.getCompoundEditors(), compare.getCompoundEditors());
381     }
382 
383     /**
384      * Compare all the keys and values of the term parameter. Returns false if any of the keys (names) or
385      * values of the term paramters is not the same.
386      *
387      * @param original list of term parameters for current term
388      * @param compare  list of term paramters to compare with.
389      * @return true if all names and values are the same.
390      */
391     @Override
392     public Boolean compareTerm(List<TermParameterEditor> original, List<TermParameterEditor> compare) {
393 
394         //If the sizes doesn't match, they are not same.
395         int originalSize = original == null ? 0 : original.size();
396         if (originalSize != (compare == null ? 0 : compare.size())) {
397             return false;
398         } else if (originalSize > 0) {
399 
400             //Compare the compound propositions.
401             BeanPropertyComparator termComparator = new BeanPropertyComparator(Arrays.asList("name"));
402             for (int index = 0; index < originalSize; index++) {
403                 if (termComparator.compare(original.get(index), compare.get(index)) != 0) {
404                     return false;
405                 }
406             }
407         }
408 
409         return true;
410 
411     }
412 
413     /**
414      * Recursively compare child propositions.
415      *
416      * @param original
417      * @param compare
418      * @return
419      */
420     @Override
421     public Boolean compareCompoundProposition(List<PropositionEditor> original, List<PropositionEditor> compare) {
422 
423         //If the sizes doesn't match, they are not same.
424         int originalSize = original == null ? 0 : original.size();
425         if (originalSize != (compare == null ? 0 : compare.size())) {
426             return false;
427         } else if (originalSize > 0) {
428 
429             //Compare the compound propositions.
430             for (int index = 0; index < originalSize; index++) {
431                 if (!compareProposition(original.get(index), compare.get(index))) {
432                     return false;
433                 }
434             }
435         }
436 
437         return true;
438     }
439 
440     /**
441      * Make a new copy of the current proposition including the compounds.
442      * <p/>
443      * The deepcopy is done to make sure that create a full copy and does not only copy the references.
444      *
445      * @param oldProposition
446      * @return
447      */
448     @Override
449     public PropositionEditor copyProposition(PropositionEditor oldProposition) {
450         try {
451             PropositionEditor newProposition = this.copyPropositionEditor(oldProposition);
452 
453             //Use a deepcopy to create new references to inner objects such as string.
454             return (PropositionEditor) ObjectUtils.deepCopy(newProposition);
455         } catch (Exception e) {
456             return null;
457         }
458     }
459 
460     /**
461      * Used when the user clicked the copy button. It creates a new copy of the proposition with all the related
462      * compound propositions.
463      * <p/>
464      * The compound propositions is handled recursively.
465      *
466      * @param oldProposition
467      * @return
468      */
469     protected PropositionEditor copyPropositionEditor(PropositionEditor oldProposition) {
470         PropositionEditor newProposition;
471         try {
472             newProposition = this.getPropositionEditorClass().newInstance();
473         } catch (Exception e) {
474             newProposition = new PropositionEditor();
475         }
476         BeanUtils.copyProperties(oldProposition, newProposition, new String[]{"key", "id", "term", "parameters"});
477 
478         if (!oldProposition.getPropositionTypeCode().equals("C")) {
479             List<PropositionParameterEditor> propositionParameterEditors = new ArrayList<PropositionParameterEditor>();
480             for (PropositionParameterEditor parm : oldProposition.getParameters()) {
481                 PropositionParameterEditor newParm = new PropositionParameterEditor();
482                 BeanUtils.copyProperties(parm, newParm, new String[]{"termValue", "id", "versionNumber"});
483                 propositionParameterEditors.add(newParm);
484             }
485 
486             newProposition.setParameters(propositionParameterEditors);
487 
488             TermEditor termEditor = new TermEditor();
489             List<TermParameterEditor> termParameterEditors = new ArrayList<TermParameterEditor>();
490            if( oldProposition.getTerm() != null) {
491             BeanUtils.copyProperties(oldProposition.getTerm(), termEditor, new String[]{"id", "versionNumber", "parameters"});
492             for (TermParameterEditor termParm : oldProposition.getTerm().getEditorParameters()) {
493                 TermParameterEditor newTermParm = new TermParameterEditor();
494                 BeanUtils.copyProperties(termParm, newTermParm, new String[]{"id", "versionNumber"});
495                 termParameterEditors.add(newTermParm);
496             }
497            }
498             termEditor.setParameters(termParameterEditors);
499 
500             newProposition.setTerm(termEditor);
501             this.resetDescription(newProposition);
502         }
503 
504         if (newProposition.getCompoundEditors() != null) {
505             List<PropositionEditor> props = new ArrayList<PropositionEditor>();
506             for (PropositionEditor prop : newProposition.getCompoundEditors()) {
507                 props.add(this.copyPropositionEditor(prop));
508             }
509             newProposition.setCompoundEditors(props);
510         }
511 
512 
513         return newProposition;
514     }
515 
516     @Override
517     public PropositionEditor createCompoundPropositionBoStub(PropositionEditor existing, boolean addNewChild) {
518         try {
519             PropositionEditor compound = PropositionTreeUtil.createCompoundPropositionBoStub(existing, addNewChild, this.getPropositionEditorClass());
520             this.setTypeForCompoundOpCode(compound, LogicalOperator.AND.getCode());
521             return compound;
522         } catch (Exception e) {
523             return null;
524         }
525     }
526 
527     @Override
528     public void setTypeForCompoundOpCode(PropositionEditor proposition, String compoundOpCode) {
529         //Return as quickly as possible for performance.
530         if (compoundOpCode.equals(proposition.getCompoundOpCode())) {
531             return;
532         }
533 
534         //Clear the natural language so the the tree builder can rebuild it.
535         proposition.getNaturalLanguage().clear();
536         proposition.setCompoundOpCode(compoundOpCode);
537     }
538 
539     /**
540      * Creates a new instance of a simple proposition.
541      *
542      * @param sibling
543      * @return
544      */
545     @Override
546     public PropositionEditor createSimplePropositionBoStub(PropositionEditor sibling) {
547         try {
548             return PropositionTreeUtil.createSimplePropositionBoStub(sibling, this.getPropositionEditorClass());
549         } catch (Exception e) {
550             return null;
551         }
552     }
553 
554     /**
555      * Override this method to return a different class type if you need to use a different propositoin editor class.
556      *
557      * @return
558      */
559     public Class<? extends PropositionEditor> getPropositionEditorClass() {
560         return PropositionEditor.class;
561     }
562 
563     protected RuleManagementService getRuleManagementService() {
564         if (ruleManagementService == null) {
565             ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(QName.valueOf("ruleManagementService"));
566         }
567         return ruleManagementService;
568     }
569 
570     protected RuleCompareTreeBuilder getCompareTreeBuilder() {
571         if (compareTreeBuilder == null) {
572             compareTreeBuilder = new RuleCompareTreeBuilder();
573         }
574         return compareTreeBuilder;
575     }
576 
577     protected RuleEditTreeBuilder getEditTreeBuilder() {
578         if (editTreeBuilder == null) {
579             editTreeBuilder = new RuleEditTreeBuilder();
580         }
581         return editTreeBuilder;
582     }
583 
584     protected RulePreviewTreeBuilder getPreviewTreeBuilder() {
585         if (previewTreeBuilder == null) {
586             previewTreeBuilder = new RulePreviewTreeBuilder();
587         }
588         return previewTreeBuilder;
589     }
590 
591     protected RuleViewTreeBuilder getViewTreeBuilder() {
592         if (viewTreeBuilder == null) {
593             viewTreeBuilder = new RuleViewTreeBuilder();
594         }
595         return viewTreeBuilder;
596     }
597 
598     protected NaturalLanguageHelper getNaturalLanguageHelper() {
599         if (naturalLanguageHelper == null) {
600             naturalLanguageHelper = new NaturalLanguageHelper();
601             naturalLanguageHelper.setRuleManagementService(this.getRuleManagementService());
602         }
603         return naturalLanguageHelper;
604     }
605 
606     protected TemplateRegistry getTemplateRegistry() {
607         if (templateRegistry == null) {
608             templateRegistry = (TemplateRegistry) GlobalResourceLoader.getService(QName.valueOf("templateResolverMockService"));
609         }
610         return templateRegistry;
611     }
612 
613     protected KrmsTypeRepositoryService getKrmsTypeRepositoryService() {
614         if (krmsTypeRepositoryService == null) {
615             krmsTypeRepositoryService = (KrmsTypeRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "krmsTypeRepositoryService"));
616         }
617         return krmsTypeRepositoryService;
618     }
619 
620     public TermRepositoryService getTermRepositoryService() {
621         if (termRepositoryService == null) {
622             termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService"));
623         }
624         return termRepositoryService;
625     }
626 
627 }