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