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  /*
17   * To change this template, choose Tools | Templates
18   * and open the template in the editor.
19   */
20  package org.kuali.rice.krms.impl.repository;
21  
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
30  import org.kuali.rice.krms.api.repository.LogicalOperator;
31  import org.kuali.rice.krms.api.repository.NaturalLanguageTree;
32  import org.kuali.rice.krms.api.repository.RuleManagementService;
33  import org.kuali.rice.krms.api.repository.TranslateBusinessMethods;
34  import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
35  import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
36  import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplate;
37  import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplaterContract;
38  import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
39  import org.kuali.rice.krms.api.repository.proposition.PropositionParameter;
40  import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
41  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
42  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
43  import org.kuali.rice.krms.api.repository.term.TermDefinition;
44  import org.kuali.rice.krms.api.repository.term.TermParameterDefinition;
45  import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
46  
47  /**
48   * @author nwright
49   */
50  public class KSTranslationUtility implements TranslateBusinessMethods {
51  
52      private RuleManagementService ruleManagementService;
53      private TermRepositoryService termRepositoryService;
54      private NaturalLanguageTemplaterContract templater;
55  
56      public KSTranslationUtility(RuleManagementService ruleManagementService, TermRepositoryService termRepositoryService,
57                                NaturalLanguageTemplaterContract templater) {
58          this.ruleManagementService = ruleManagementService;
59          this.termRepositoryService = termRepositoryService;
60          this.templater = templater;
61      }
62  
63      public RuleManagementService getRuleManagementService() {
64          return ruleManagementService;
65      }
66  
67      public void setRuleManagementService(RuleManagementService ruleManagementService) {
68          this.ruleManagementService = ruleManagementService;
69      }
70  
71      public NaturalLanguageTemplaterContract getTemplater() {
72          return templater;
73      }
74  
75      public void setTemplater(NaturalLanguageTemplaterContract templater) {
76          this.templater = templater;
77      }
78  
79      @Override
80      public String translateNaturalLanguageForObject(String naturalLanguageUsageId, String typeId, String krmsObjectId, String languageCode)
81              throws RiceIllegalArgumentException {
82  
83          Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode);
84  
85          if (typeId.equals("agenda")) {
86              AgendaDefinition agenda = this.ruleManagementService.getAgenda(krmsObjectId);
87              if (agenda == null) {
88                  throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for an agenda");
89              }
90              return this.translateNaturalLanguageForAgenda(agenda, templateMap);
91          } else if (typeId.equals("rule")) {
92              RuleDefinition rule = this.ruleManagementService.getRule(krmsObjectId);
93              if (rule == null) {
94                  throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a rule");
95              }
96              return this.translateNaturalLanguageForRule(rule, templateMap);
97          } else if (typeId.equals("proposition")) {
98              PropositionDefinition proposition = this.ruleManagementService.getProposition(krmsObjectId);
99              if (proposition == null) {
100                 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a proposition");
101             }
102             return this.translateNaturalLanguageForProposition(naturalLanguageUsageId, proposition, languageCode);
103         }
104 
105         return StringUtils.EMPTY;
106     }
107 
108     protected String translateNaturalLanguageForAgenda(AgendaDefinition agenda, Map<String, NaturalLanguageTemplate> templateMap) throws RiceIllegalArgumentException {
109         if (agenda.getFirstItemId() == null) {
110             throw new RiceIllegalArgumentException("Agenda has no first item");
111         }
112 
113         AgendaItemDefinition item = this.ruleManagementService.getAgendaItem(agenda.getFirstItemId());
114         return translateNaturalLanguageForAgendaItem(item, templateMap);
115     }
116 
117     protected String translateNaturalLanguageForAgendaItem(AgendaItemDefinition item, Map<String, NaturalLanguageTemplate> templateMap) {
118         if(item==null){
119             return StringUtils.EMPTY;
120         }
121 
122         String naturalLanguage = StringUtils.EMPTY;
123         if (item.getRuleId() != null) {
124             RuleDefinition rule = this.ruleManagementService.getRule(item.getRuleId());
125             naturalLanguage += this.translateNaturalLanguageForRule(rule, templateMap);
126         }
127         naturalLanguage += translateNaturalLanguageForAgendaItem(item.getWhenTrue(), templateMap);
128         naturalLanguage += translateNaturalLanguageForAgendaItem(item.getWhenFalse(), templateMap);
129         naturalLanguage += translateNaturalLanguageForAgendaItem(item.getAlways(), templateMap);
130         return naturalLanguage;
131     }
132 
133     protected String translateNaturalLanguageForRule(RuleDefinition rule, Map<String, NaturalLanguageTemplate> templateMap) throws RiceIllegalArgumentException {
134         if(rule==null){
135             return StringUtils.EMPTY;
136         }
137 
138         NaturalLanguageTemplate nlTemplate = templateMap.get(rule.getTypeId());
139         String naturalLanguage = nlTemplate.getTemplate() + " ";
140 
141         if(rule.getProposition()!=null){
142             naturalLanguage += this.translateNaturalLanguageForProposition(rule.getProposition(), templateMap, true) + ". ";
143         }
144 
145         return naturalLanguage;
146     }
147 
148     @Override
149     public String translateNaturalLanguageForProposition(String naturalLanguageUsageId,
150                                                          PropositionDefinition proposition, String languageCode)
151             throws RiceIllegalArgumentException {
152 
153         Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode);
154         return translateNaturalLanguageForProposition(proposition, templateMap, true) + ". ";
155     }
156 
157     private Map<String, NaturalLanguageTemplate> getNaturalLanguageTemplateMap(String naturalLanguageUsageId, String languageCode) {
158         Map<String, NaturalLanguageTemplate> templateMap = new HashMap<String, NaturalLanguageTemplate>();
159         List<NaturalLanguageTemplate> templates = this.ruleManagementService.findNaturalLanguageTemplatesByNaturalLanguageUsage(naturalLanguageUsageId);
160         for(NaturalLanguageTemplate nlTemplate : templates){
161             if(languageCode.equals(nlTemplate.getLanguageCode())){
162                 templateMap.put(nlTemplate.getTypeId(), nlTemplate);
163             }
164         }
165         return templateMap;
166     }
167 
168     /**
169      * These constants declared here for Rice if they would like to move them
170      * to a constants class of their choice. Need to be able to add a
171      * blank template in the DB. Rice validations counter it.
172      */
173      private final String KRMS_NL_TEMP_BLANK = "kuali.krms.nl.template.blank";
174      private final String KRMS_NL_TEMP_ATTR_OPERATOR = "kuali.krms.nl.template.attribute.operator";
175 
176     /**
177      * This method is added because from a functional point of view the root proposition is ignored when it is a group
178      * and therefore handled differently.
179      *
180      * @param proposition
181      * @param templateMap
182      * @param isRoot
183      * @return
184      */
185     private String translateNaturalLanguageForProposition(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap, boolean isRoot) {
186         NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId());
187 
188         StringBuilder naturalLanguage = new StringBuilder();
189         if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
190             if(naturalLanguageTemplate!=null){
191                 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition);
192                 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap));
193             }
194 
195         } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
196 
197             if(naturalLanguageTemplate!=null && !naturalLanguageTemplate.getTemplate().equals(KRMS_NL_TEMP_BLANK)){
198                 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap);
199                 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap));
200             }
201 
202             //Null check because newly created compound propositions should also be translateable.
203             if(proposition.getCompoundComponents()!=null){
204                 /*
205                   Take note when working in this part of the method AND only for Final Matrix Exam. The same idea is carried out in
206                   FERuleViewHelperServiceImpl.getDescriptionForPropositionTree
207                   ln: 218 but that method is mainly used with data that is in memory whereas this method makes DB call.
208                  */
209                 String operator = getCompoundSeperator(naturalLanguageTemplate, isRoot);
210 
211                 for (PropositionDefinition child : proposition.getCompoundComponents()) {
212                     if(proposition.getCompoundComponents().indexOf(child)!=0){
213                         naturalLanguage.append(operator);
214                     }
215                     naturalLanguage.append(this.translateNaturalLanguageForProposition(child, templateMap, false));
216                 }
217             }
218 
219         } else {
220             throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode());
221         }
222 
223         return naturalLanguage.toString();
224     }
225 
226     private String getCompoundSeperator(NaturalLanguageTemplate naturalLanguageTemplate, boolean isRoot) {
227         String operator = naturalLanguageTemplate.getAttributes().get(KRMS_NL_TEMP_ATTR_OPERATOR);
228         if (isRoot){
229             return ". " + operator + " ";
230         }
231         return "; " + operator + " ";
232     }
233 
234 
235     @Override
236     public NaturalLanguageTree translateNaturalLanguageTreeForProposition(String naturalLanguageUsageId,
237                                                                           PropositionDefinition proposition,
238                                                                           String languageCode) throws RiceIllegalArgumentException {
239 
240         Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode);
241         return translateNaturalLanguageTreeForProposition(proposition, templateMap);
242     }
243 
244     public NaturalLanguageTree translateNaturalLanguageTreeForProposition(PropositionDefinition proposition,
245                                                                           Map<String, NaturalLanguageTemplate> templateMap) throws RiceIllegalArgumentException {
246 
247         NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId());
248 
249         NaturalLanguageTree.Builder tree = NaturalLanguageTree.Builder.create();
250         if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
251             Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition);
252             String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap);
253             tree.setNaturalLanguage(naturalLanguage);
254 
255         } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
256             Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap);
257             String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap);
258             tree.setNaturalLanguage(naturalLanguage);
259 
260             //Null check because newly created compound propositions should also be translateable.
261             if(proposition.getCompoundComponents()!=null){
262                 List<NaturalLanguageTree> children = new ArrayList<NaturalLanguageTree>();
263                 for (PropositionDefinition child : proposition.getCompoundComponents()) {
264                     children.add(this.translateNaturalLanguageTreeForProposition(child, templateMap));
265                 }
266                 tree.setChildren(children);
267             }
268 
269         } else {
270             throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode());
271         }
272 
273         return tree.build();
274     }
275 
276     protected Map<String, Object> buildSimplePropositionContextMap(PropositionDefinition proposition) {
277         if (!proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
278             throw new RiceIllegalArgumentException("proposition is not simple " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription());
279         }
280         Map<String, Object> contextMap = new LinkedHashMap<String, Object>();
281         for (PropositionParameter param : proposition.getParameters()) {
282             if (param.getParameterType().equals(PropositionParameterType.TERM.getCode())) {
283                 TermDefinition term = param.getTermValue();
284                 if ((term == null) && (StringUtils.isNotBlank(param.getValue()))) {
285                     term = this.termRepositoryService.getTerm(param.getValue());
286                 }
287                 if (term != null) {
288                     for (TermParameterDefinition termParam : term.getParameters()) {
289                         contextMap.put(termParam.getName(), termParam.getValue());
290                     }
291                 } else {
292                     contextMap.put(param.getParameterType(), param.getValue());
293                 }
294             } else {
295                 contextMap.put(param.getParameterType(), param.getValue());
296             }
297         }
298         return contextMap;
299     }
300 
301     protected Map<String, Object> buildCompoundPropositionContextMap(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap) {
302         if (!proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
303             throw new RiceIllegalArgumentException("proposition us not compound " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription());
304         }
305         return new LinkedHashMap<String, Object>();
306     }
307 
308 }