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