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.student.lum.lu.ui.krms.service.impl;
17  
18  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
19  import org.kuali.rice.krad.uif.container.Group;
20  import org.kuali.rice.krad.uif.element.Action;
21  import org.kuali.rice.krad.uif.field.Field;
22  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils;
23  import org.kuali.rice.krad.uif.util.LifecycleElement;
24  import org.kuali.rice.krad.uif.view.ViewModel;
25  import org.kuali.rice.krad.util.GlobalVariables;
26  import org.kuali.rice.krad.util.ObjectUtils;
27  import org.kuali.rice.krms.api.repository.LogicalOperator;
28  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
29  import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
30  import org.kuali.rice.krms.dto.PropositionEditor;
31  import org.kuali.rice.krms.dto.RuleEditor;
32  import org.kuali.rice.krms.dto.TermEditor;
33  import org.kuali.rice.krms.dto.TermParameterEditor;
34  import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
35  import org.kuali.rice.krms.service.impl.RuleViewHelperServiceImpl;
36  import org.kuali.rice.krms.tree.RuleEditTreeBuilder;
37  import org.kuali.rice.krms.tree.RulePreviewTreeBuilder;
38  import org.kuali.rice.krms.tree.RuleViewTreeBuilder;
39  import org.kuali.rice.krms.util.PropositionTreeUtil;
40  import org.kuali.student.common.util.security.ContextUtils;
41  import org.kuali.student.core.krms.dto.KSPropositionEditor;
42  import org.kuali.student.core.krms.tree.KSRuleEditTreeBuilder;
43  import org.kuali.student.core.krms.tree.KSRulePreviewTreeBuilder;
44  import org.kuali.student.core.krms.tree.KSRuleViewTreeBuilder;
45  import org.kuali.student.lum.lu.ui.krms.dto.CluInformation;
46  import org.kuali.student.lum.lu.ui.krms.dto.CluSetWrapper;
47  import org.kuali.student.lum.lu.ui.krms.dto.LUPropositionEditor;
48  import org.kuali.student.lum.lu.ui.krms.dto.TestWrapper;
49  import org.kuali.student.lum.lu.ui.krms.tree.LURuleEditTreeBuilder;
50  import org.kuali.student.lum.lu.ui.krms.tree.LURulePreviewTreeBuilder;
51  import org.kuali.student.lum.lu.ui.krms.tree.LURuleViewTreeBuilder;
52  import org.kuali.student.lum.lu.ui.krms.util.CluInformationHelper;
53  import org.kuali.student.lum.lu.ui.krms.util.CluSearchUtil;
54  import org.kuali.student.lum.lu.ui.krms.util.LUKRMSConstants;
55  import org.kuali.student.r1.common.rice.StudentIdentityConstants;
56  import org.kuali.student.r2.common.dto.ContextInfo;
57  import org.kuali.student.r2.common.exceptions.OperationFailedException;
58  import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
59  import org.kuali.student.r2.core.constants.OrganizationServiceConstants;
60  import org.kuali.student.r2.core.organization.service.OrganizationService;
61  import org.kuali.student.r2.core.search.dto.SearchParamInfo;
62  import org.kuali.student.r2.core.search.dto.SearchRequestInfo;
63  import org.kuali.student.r2.core.search.dto.SearchResultCellInfo;
64  import org.kuali.student.r2.core.search.dto.SearchResultInfo;
65  import org.kuali.student.r2.core.search.dto.SearchResultRowInfo;
66  import org.kuali.student.r2.lum.clu.dto.CluSetInfo;
67  import org.kuali.student.r2.lum.clu.dto.MembershipQueryInfo;
68  import org.kuali.student.r2.lum.clu.service.CluService;
69  import org.kuali.student.r2.lum.lrc.service.LRCService;
70  import org.kuali.student.r2.lum.util.constants.CluServiceConstants;
71  import org.kuali.student.r2.lum.util.constants.LrcServiceConstants;
72  
73  import javax.xml.namespace.QName;
74  import java.util.ArrayList;
75  import java.util.Collections;
76  import java.util.Comparator;
77  import java.util.List;
78  
79  /**
80   * Helpers Service with enrolment specific functions for the Rule Pages.
81   *
82   * @author Kuali Student Team
83   */
84  public class LURuleViewHelperServiceImpl extends RuleViewHelperServiceImpl {
85  
86      private CluService cluService;
87      private ContextInfo contextInfo;
88      private OrganizationService organizationService;
89  
90      private CluInformationHelper cluInfoHelper;
91  
92      private KSRulePreviewTreeBuilder previewTreeBuilder;
93      private KSRuleViewTreeBuilder viewTreeBuilder;
94      private KSRuleEditTreeBuilder editTreeBuilder;
95  
96      @Override
97      public Class<? extends PropositionEditor> getPropositionEditorClass() {
98          return KSPropositionEditor.class;
99      }
100 
101     @Override
102     public void performCustomApplyModel(LifecycleElement element, Object model) {
103         super.performCustomApplyModel(element, model);
104 
105         if(element instanceof Group) {
106             Group group = (Group) element;
107 
108             if(group.isReadOnly()) {
109                 processGroupItems(group);
110             }
111         }
112     }
113 
114     protected void processGroupItems(Group group) {
115         List<Field> fields = ViewLifecycleUtils.getElementsOfTypeDeep(group.getItems(), Field.class);
116         for(Field field : fields) {
117             field.setReadOnly(true);
118         }
119 
120         List<Action> actions = ViewLifecycleUtils.getElementsOfTypeDeep(group.getItems(), Action.class);
121         for(Action action : actions) {
122             action.setRender(false);
123         }
124     }
125 
126     @Override
127     public PropositionEditor copyProposition(PropositionEditor oldProposition) {
128         try {
129             LUPropositionEditor newProposition = (LUPropositionEditor) this.copyPropositionEditor(oldProposition);
130 
131             //Set the cluset to null to force the builder to create a new cluset.
132             nullifyCluSetInfo(newProposition);
133 
134             //Use a deepcopy to create new references to inner objects such as string.
135             return (PropositionEditor) ObjectUtils.deepCopy(newProposition);
136         } catch (Exception e) {
137             return null;
138         }
139     }
140 
141     @Override
142     public void setTypeForCompoundOpCode(PropositionEditor proposition, String compoundOpCode) {
143         super.setTypeForCompoundOpCode(proposition, compoundOpCode);
144         if (LogicalOperator.AND.getCode().equalsIgnoreCase(compoundOpCode)) {
145             proposition.setType(KSKRMSServiceConstants.PROPOSITION_TYPE_COMPOUND_AND);
146         } else if (LogicalOperator.OR.getCode().equalsIgnoreCase(compoundOpCode)) {
147             proposition.setType(KSKRMSServiceConstants.PROPOSITION_TYPE_COMPOUND_OR);
148         }
149         try {
150             KrmsTypeDefinition type = KrmsRepositoryServiceLocator.getKrmsTypeRepositoryService().getTypeByName(StudentIdentityConstants.KS_NAMESPACE_CD, proposition.getType());
151             proposition.setTypeId(type.getId());
152         } catch (Exception e) {
153             //ignore if service not available.
154         }
155 
156     }
157 
158     /**
159      * Method to recursively set proposition's cluSetInfo to null.
160      *
161      * @param propositionEditor
162      */
163     protected void nullifyCluSetInfo(LUPropositionEditor propositionEditor) {
164 
165         //Set cluSetInfo recursively to null to force builder to create new cluset.
166         if(propositionEditor.getCourseSet()!=null){
167             propositionEditor.getCourseSet().clear();
168         } else if(propositionEditor.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
169             for(int i = 0; i < propositionEditor.getCompoundEditors().size(); i++) {
170                 LUPropositionEditor prop = (LUPropositionEditor) propositionEditor.getCompoundEditors().get(i);
171                 if(prop.getCourseSet() != null) {
172                     prop.getCourseSet().clear();
173                 } else if(prop.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
174                     nullifyCluSetInfo(prop);
175                 }
176             }
177         }
178     }
179 
180     protected void checkNaturalLanguageForTree(RuleEditor ruleEditor) {
181         if ((ruleEditor !=null) && (ruleEditor.getProposition()!=null)){
182             PropositionEditor originalRoot = ruleEditor.getPropositionEditor();
183             if (!originalRoot.getNaturalLanguage().containsKey(this.getEditTreeBuilder().getNaturalLanguageUsageKey())) {
184                 this.getNaturalLanguageHelper().setNaturalLanguageTreeForUsage(originalRoot, this.getEditTreeBuilder().getNaturalLanguageUsageKey(), StudentIdentityConstants.KS_NAMESPACE_CD);
185             }
186         }
187     }
188 
189     /**
190      * Compares CO and CLU with each other for the display of a info message.
191      *
192      * @param original
193      * @param compare
194      * @return
195      */
196     @Override
197     public Boolean compareProposition(PropositionEditor original, PropositionEditor compare) {
198         //If proposition contains cluset or programCluset, skip super compare method else use super compare method for all simple propositions
199         if(((LUPropositionEditor) original).getCourseSet() == null && ((LUPropositionEditor) original).getProgramSet() == null) {
200             if(!super.compareProposition(original, compare)) {
201                 return false;
202             }
203         } //If proposition contains cluset or programCluset, compare typeId's
204         else if(!original.getTypeId().equals(compare.getTypeId())) {
205             return false;
206         } //If proposition contains cluset or programCluset and is not a compound proposition, compare multicourse proposition properties
207         else if(!original.getPropositionTypeCode().equals("C")) {
208             LUPropositionEditor enrolOriginal = (LUPropositionEditor) original;
209 
210             //Populate compare proposition cluSetInformation for comparison
211             if(enrolOriginal.getCourseSet() != null) {
212                 if(enrolOriginal.getCourseSetParent() == null) {
213                     TermEditor term = new TermEditor(PropositionTreeUtil.getTermParameter(compare.getParameters()).getTermValue());
214                     for(TermParameterEditor termParameterEditor : term.getEditorParameters()) {
215                         if(termParameterEditor.getName().equals(KSKRMSServiceConstants.TERM_PARAMETER_TYPE_COURSE_CLUSET_KEY)) {
216                             enrolOriginal.setCourseSetParent(this.getCluInfoHelper().getCluSetWrapper(termParameterEditor.getValue()));
217                             break;
218                         }
219                     }
220                 }
221                 //If compare and original propositions are not null compare CluSetInformation
222                 if(enrolOriginal.getCourseSetParent() != null) {
223                     //Compare propositions CluSetInformation clu's
224                     if(!enrolOriginal.getCourseSet().getCluDelimitedString().equals(enrolOriginal.getCourseSetParent().getCluDelimitedString())) {
225                         return false;
226                     }
227                     //Compare propositions CluSetInformation cluSets
228                     if(!enrolOriginal.getCourseSet().getCluSetDelimitedString().equals(enrolOriginal.getCourseSetParent().getCluSetDelimitedString())) {
229                         return false;
230                     }
231                 }
232             }
233 
234             //Populate compare proposition Program CluSetInformation for comparison
235             if(enrolOriginal.getProgramSet() != null) {
236                 if(enrolOriginal.getProgramSetParent() == null) {
237                     TermEditor term = new TermEditor(PropositionTreeUtil.getTermParameter(compare.getParameters()).getTermValue());
238                     for(TermParameterEditor termParameterEditor : term.getEditorParameters()) {
239                         if(termParameterEditor.getName().equals(KSKRMSServiceConstants.TERM_PARAMETER_TYPE_COURSE_CLUSET_KEY)) {
240                             enrolOriginal.setProgramSetParent(this.getCluInfoHelper().getCluSetWrapper(termParameterEditor.getValue()));
241                             break;
242                         }
243                     }
244                 }
245                 //If compare and original propositions are not null compare ProgramCluSetInformation
246                 if(enrolOriginal.getProgramSetParent() != null) {
247                     //Compare propositions ProgramCluSetInformation clu's
248                     if(!enrolOriginal.getProgramSet().getCluDelimitedString().equals(enrolOriginal.getProgramSetParent().getCluDelimitedString())) {
249                         return false;
250                     }
251                     //Compare propositions ProgramCluSetInformation cluSets
252                     if(!enrolOriginal.getProgramSet().getCluSetDelimitedString().equals(enrolOriginal.getProgramSetParent().getCluSetDelimitedString())) {
253                         return false;
254                     }
255                 }
256             }
257         }
258 
259         return true;
260     }
261 
262     @Override
263     protected boolean performAddLineValidation(ViewModel viewModel, Object newLine, String collectionId,
264                                                String collectionPath) {
265 
266         if(!super.performAddLineValidation(viewModel, newLine, collectionId, collectionPath)) {
267             return false;
268         }
269 
270         if(newLine instanceof  CluInformation) {
271             CluInformation clu = (CluInformation) newLine;
272             if(!validateNewClu(viewModel, clu, collectionId)){
273                 return false;
274             }
275 
276             if(collectionPath.endsWith(LUKRMSConstants.KSKRMS_PROPERTY_NAME_CLUS)) {
277                 return validateNewCourse(viewModel, (CluInformation) newLine, collectionId);
278             } else if (collectionPath.endsWith(LUKRMSConstants.KSKRMS_PROPERTY_NAME_PROG_CLUS)) {
279                 return validateNewProgram(viewModel, (CluInformation) newLine, collectionId);
280             }
281         } else if (newLine instanceof CluSetWrapper){
282             return validateNewCluSet(viewModel, (CluSetWrapper) newLine, collectionId);
283         }
284 
285         return true;
286     }
287 
288     protected boolean validateNewClu(ViewModel viewModel, CluInformation clu, String collectionId) {
289         //Check if this is a valid clu code.
290         if((clu.getCode()==null)||(clu.getCode().isEmpty())){
291             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_APPROVED_COURSE_REQUIRED);
292             return false;
293         } else {
294             // convert term-code to UPPERCASE
295             clu.setCode(clu.getCode().toUpperCase());
296         }
297 
298         return true;
299     }
300 
301     protected boolean validateNewCourse(ViewModel viewModel, CluInformation clu, String collectionId) {
302         //Check if this is a valid course.
303         CluInformation searchClu = null;
304         try{
305             searchClu = this.getCluInfoHelper().getCluInfoForCodeAndType(clu.getCode(), CluSearchUtil.getCluTypesForCourse());
306         }catch(OperationFailedException ofe){
307             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_MULTIPLE_RESULTS_FOR_CODE);
308         }
309         if(searchClu==null){
310             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_APPROVED_COURSE_CODE_INVALID);
311             return false;
312         } else {
313             clu.setCluId(searchClu.getCluId());
314             clu.setVerIndependentId(searchClu.getVerIndependentId());
315             clu.setShortName(searchClu.getShortName());
316             clu.setDescription(searchClu.getDescription());
317         }
318 
319         return true;
320     }
321 
322     protected boolean validateNewCluSet(ViewModel viewModel, CluSetWrapper cluSet, String collectionId) {
323         //Check if this is a valid clu.
324         if((cluSet.getId() == null)||(cluSet.getId().isEmpty())){
325             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_COURSESETS_REQUIRED);
326             return false;
327         }
328 
329         CluSetInfo searchCluSet = this.getCluInfoHelper().getCluSetForId(cluSet.getId());
330         if (searchCluSet == null) {
331             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_APPROVED_COURSE_CODE_INVALID);
332             return false;
333         } else {
334             cluSet.setClus(this.getCluInfoHelper().getCluInfos(searchCluSet.getCluIds()));
335             cluSet.setName(searchCluSet.getName());
336         }
337 
338         return true;
339     }
340 
341     protected boolean validateNewProgram(ViewModel viewModel, CluInformation clu, String collectionId) {
342         //Check if this is a valid program.
343         CluInformation searchClu = null;
344         try{
345             searchClu = this.getCluInfoHelper().getCluInfoForCodeAndType(clu.getCode(), CluSearchUtil.getCluTypesForProgram());
346         }catch(OperationFailedException ofe){
347             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_MULTIPLE_RESULTS_FOR_CODE);
348         }
349         if(searchClu==null){
350             GlobalVariables.getMessageMap().putErrorForSectionId(collectionId, LUKRMSConstants.KSKRMS_MSG_ERROR_APPROVED_PROGRAM_CODE_INVALID);
351             return false;
352         } else {
353             clu.setCluId(searchClu.getCluId());
354             clu.setVerIndependentId(searchClu.getVerIndependentId());
355             clu.setShortName(searchClu.getShortName());
356             clu.setDescription(searchClu.getDescription());
357         }
358 
359         return true;
360     }
361 
362     @Override
363     public void processAfterAddLine(ViewModel model, Object lineObject, String collectionId, String collectionPath,
364                                     boolean isValidLine) {
365 
366         if ((lineObject instanceof CluInformation) && (collectionPath.endsWith(LUKRMSConstants.KSKRMS_PROPERTY_NAME_CLUS))) {
367             //Sort the clus.
368             RuleEditor ruleEditor = this.getRuleEditor(model);
369             LUPropositionEditor propEditor = (LUPropositionEditor) PropositionTreeUtil.getProposition(ruleEditor);
370 
371             CluInformation clu = (CluInformation) lineObject;
372             if (clu.getCluId() != null) {
373                 clu.setCredits(this.getCluInfoHelper().getCreditInfo(clu.getCluId()));
374                 Collections.sort(propEditor.getCourseSet().getClus());
375 
376             }
377         } else if (lineObject instanceof CluSetWrapper) {
378             //Set the clus on the wrapper object.
379             CluSetWrapper cluSet = (CluSetWrapper) lineObject;
380             if (cluSet.getId() != null) {
381 
382                 //Sort the clus.
383                 RuleEditor ruleEditor = this.getRuleEditor(model);
384                 LUPropositionEditor propEditor = (LUPropositionEditor) PropositionTreeUtil.getProposition(ruleEditor);
385                 Collections.sort(propEditor.getCourseSet().getCluSets(), new Comparator<CluSetWrapper>() {
386 
387                     @Override
388                     public int compare(CluSetWrapper o1, CluSetWrapper o2) {
389                         return o1.getName().compareTo(o2.getName());
390                     }
391                 });
392             }
393         } else if ((lineObject instanceof CluInformation) && (collectionPath.endsWith(LUKRMSConstants.KSKRMS_PROPERTY_NAME_PROG_CLUS))) {
394             //Sort the clus.
395             RuleEditor ruleEditor = this.getRuleEditor(model);
396             LUPropositionEditor propEditor = (LUPropositionEditor) PropositionTreeUtil.getProposition(ruleEditor);
397 
398             CluInformation clu = (CluInformation) lineObject;
399             if (clu.getCluId() != null) {
400                 clu.setCredits(this.getCluInfoHelper().getCreditInfo(clu.getCluId()));
401                 Collections.sort(propEditor.getProgramSet().getClus());
402             }
403         }
404 
405     }
406 
407     public List<CluInformation> getCoursesInRange(MembershipQueryInfo membershipQuery) {
408         return this.getCluInfoHelper().getCluInfosWithDetailForQuery(membershipQuery);
409     }
410 
411     public List<TestWrapper> getTestNamesForSuggest(String testName) {
412 
413         List<TestWrapper> displays = new ArrayList<TestWrapper>();
414         List<SearchParamInfo> queryParamValueList = new ArrayList<SearchParamInfo>();
415         SearchParamInfo testNameParam = new SearchParamInfo();
416         testNameParam.setKey("cluset.queryParam.optionalName");
417         testNameParam.getValues().add(testName);
418         queryParamValueList.add(testNameParam);
419         SearchParamInfo reusableParam = new SearchParamInfo();
420         reusableParam.setKey("cluset.queryParam.optionalReusable");
421         reusableParam.getValues().add(Boolean.TRUE.toString());
422         queryParamValueList.add(reusableParam);
423         SearchParamInfo cluSetTypeParam = new SearchParamInfo();
424         cluSetTypeParam.setKey("cluset.queryParam.optionalType");
425         cluSetTypeParam.getValues().add(CluServiceConstants.CLUSET_TYPE_TEST);
426         queryParamValueList.add(cluSetTypeParam);
427 
428         SearchRequestInfo searchRequest = new SearchRequestInfo();
429         searchRequest.setSearchKey("cluset.search.generic");
430         searchRequest.setParams(queryParamValueList);
431 
432         try {
433             SearchResultInfo clus = getCluService().search(searchRequest, getContextInfo());
434             for (SearchResultRowInfo result : clus.getRows()) {
435                 List<SearchResultCellInfo> cells = result.getCells();
436                 TestWrapper display = new TestWrapper();
437                 for (SearchResultCellInfo cell : cells) {
438                     if ("cluset.resultColumn.cluSetId".equals(cell.getKey())) {
439                         display.setId(cell.getValue());
440                     } else if ("cluset.resultColumn.name".equals(cell.getKey())) {
441                         display.setDisplayName(cell.getValue());
442                     }
443                 }
444                 displays.add(display);
445             }
446         } catch (Exception e) {
447             throw new RuntimeException(e);
448         }
449         return displays;
450     }
451 
452     protected CluInformationHelper getCluInfoHelper() {
453         if (cluInfoHelper == null) {
454             cluInfoHelper = new CluInformationHelper();
455             cluInfoHelper.setCluService(this.getCluService());
456             LRCService lrcService = GlobalResourceLoader.getService(new QName(LrcServiceConstants.NAMESPACE, LrcServiceConstants.SERVICE_NAME_LOCAL_PART));
457             cluInfoHelper.setLrcService(lrcService);
458         }
459         return cluInfoHelper;
460     }
461 
462     protected CluService getCluService() {
463         if (cluService == null) {
464             cluService = GlobalResourceLoader.getService(new QName(CluServiceConstants.CLU_NAMESPACE, CluServiceConstants.SERVICE_NAME_LOCAL_PART));
465         }
466         return cluService;
467     }
468 
469     protected ContextInfo getContextInfo() {
470         if (null == contextInfo) {
471             contextInfo = ContextUtils.createDefaultContextInfo();
472         }
473         return contextInfo;
474     }
475 
476     protected OrganizationService getOrganizationService() {
477         if (organizationService == null) {
478             organizationService = GlobalResourceLoader.getService(new QName(OrganizationServiceConstants.NAMESPACE, OrganizationServiceConstants.SERVICE_NAME_LOCAL_PART));
479         }
480         return organizationService;
481     }
482 
483     @Override
484     public RulePreviewTreeBuilder getPreviewTreeBuilder() {
485         if (previewTreeBuilder == null) {
486             previewTreeBuilder = new LURulePreviewTreeBuilder();
487             previewTreeBuilder.setNlHelper(this.getNaturalLanguageHelper());
488         }
489         return previewTreeBuilder;
490     }
491 
492     @Override
493     protected RuleViewTreeBuilder getViewTreeBuilder() {
494         if (viewTreeBuilder == null) {
495             viewTreeBuilder = new LURuleViewTreeBuilder();
496             viewTreeBuilder.setNlHelper(this.getNaturalLanguageHelper());
497         }
498         return viewTreeBuilder;
499     }
500 
501     @Override
502     protected RuleEditTreeBuilder getEditTreeBuilder() {
503         if (editTreeBuilder == null) {
504             editTreeBuilder = new LURuleEditTreeBuilder();
505             editTreeBuilder.setNlHelper(this.getNaturalLanguageHelper());
506         }
507         return editTreeBuilder;
508     }
509 
510 }