View Javadoc
1   /**
2    * Copyright 2005-2014 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 TranslationUtility implements TranslateBusinessMethods {
50  
51      private RuleManagementService ruleManagementService;
52      private TermRepositoryService termRepositoryService;
53      private NaturalLanguageTemplaterContract templater;
54  
55      public TranslationUtility(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          // TODO: find out what RICE intended for this typeId? Was it supposed to be the Simple Class name?
83          if (typeId.equals("agenda")) {
84              AgendaDefinition agenda = this.ruleManagementService.getAgenda(krmsObjectId);
85              if (agenda == null) {
86                  throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for an agenda");
87              }
88              return this.translateNaturalLanguageForAgenda(naturalLanguageUsageId, agenda, languageCode);
89          } else if (typeId.equals("rule")) {
90              RuleDefinition rule = this.ruleManagementService.getRule(krmsObjectId);
91              if (rule == null) {
92                  throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a rule");
93              }
94              return this.translateNaturalLanguageForRule(naturalLanguageUsageId, rule, languageCode);
95          } else if (typeId.equals("proposition")) {
96              PropositionDefinition proposition = this.ruleManagementService.getProposition(krmsObjectId);
97              if (proposition == null) {
98                  throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a proposition");
99              }
100             return this.translateNaturalLanguageForProposition(naturalLanguageUsageId, proposition, languageCode);
101         }
102 
103         return StringUtils.EMPTY;
104     }
105 
106     protected String translateNaturalLanguageForAgenda(String naturalLanguageUsageId, AgendaDefinition agenda, String languageCode) throws RiceIllegalArgumentException {
107         if (agenda.getFirstItemId() == null) {
108             throw new RiceIllegalArgumentException("Agenda has no first item");
109         }
110 
111         AgendaItemDefinition item = this.ruleManagementService.getAgendaItem(agenda.getFirstItemId());
112         return translateNaturalLanguageForAgendaItem(naturalLanguageUsageId, item, languageCode);
113     }
114 
115     protected String translateNaturalLanguageForAgendaItem(String naturalLanguageUsageId, AgendaItemDefinition item, String languageCode) {
116         if(item==null){
117             return StringUtils.EMPTY;
118         }
119 
120         String naturalLanguage = StringUtils.EMPTY;
121         if (item.getRuleId() != null) {
122             RuleDefinition rule = this.ruleManagementService.getRule(item.getRuleId());
123             naturalLanguage += this.translateNaturalLanguageForRule(naturalLanguageUsageId, rule, languageCode);
124         }
125         naturalLanguage += translateNaturalLanguageForAgendaItem(naturalLanguageUsageId, item.getWhenTrue(), languageCode);
126         naturalLanguage += translateNaturalLanguageForAgendaItem(naturalLanguageUsageId, item.getWhenFalse(), languageCode);
127         naturalLanguage += translateNaturalLanguageForAgendaItem(naturalLanguageUsageId, item.getAlways(), languageCode);
128         return naturalLanguage;
129     }
130 
131     protected String translateNaturalLanguageForRule(String naturalLanguageUsageId, RuleDefinition rule, String languageCode) throws RiceIllegalArgumentException {
132         if(rule==null){
133             return StringUtils.EMPTY;
134         }
135 
136         NaturalLanguageTemplate nlTemplate = ruleManagementService.findNaturalLanguageTemplateByLanguageCodeTypeIdAndNluId(languageCode, rule.getTypeId(), naturalLanguageUsageId);
137         String naturalLanguage = nlTemplate.getTemplate() + " ";
138 
139         if(rule.getProposition()!=null){
140             naturalLanguage += this.translateNaturalLanguageForProposition(naturalLanguageUsageId, rule.getProposition(), languageCode);
141         }
142 
143         return naturalLanguage;
144     }
145 
146     @Override
147     public String translateNaturalLanguageForProposition(String naturalLanguageUsageId,
148             PropositionDefinition proposition, String languageCode)
149             throws RiceIllegalArgumentException {
150         return translateNaturalLanguageForProposition(naturalLanguageUsageId, proposition, languageCode, true) + ". ";
151     }
152 
153     /**
154      * This method is added because from a functional point of view the root proposition is ignored when it is a group
155      * and therefore handled differently.
156      *
157      * @param naturalLanguageUsageId
158      * @param proposition
159      * @param languageCode
160      * @param isRoot
161      * @return
162      */
163     private String translateNaturalLanguageForProposition(String naturalLanguageUsageId, PropositionDefinition proposition, String languageCode, boolean isRoot) {
164         NaturalLanguageTemplate naturalLanguageTemplate = this.ruleManagementService.findNaturalLanguageTemplateByLanguageCodeTypeIdAndNluId(
165                 languageCode, proposition.getTypeId(), naturalLanguageUsageId);
166 
167         StringBuilder naturalLanguage = new StringBuilder();
168         if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
169             if(naturalLanguageTemplate!=null){
170                 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition);
171                 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap));
172             }
173 
174         } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
175             if(naturalLanguageTemplate!=null){
176                 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(naturalLanguageUsageId, proposition, languageCode);
177                 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap));
178             }
179 
180             //Null check because newly created compound propositions should also be translateable.
181             if(proposition.getCompoundComponents()!=null){
182                 String operator = getCompoundSeperator(proposition, isRoot);
183                 for (PropositionDefinition child : proposition.getCompoundComponents()) {
184                     if(proposition.getCompoundComponents().indexOf(child)!=0){
185                         naturalLanguage.append(operator);
186                     }
187                     naturalLanguage.append(this.translateNaturalLanguageForProposition(naturalLanguageUsageId, child, languageCode, false));
188                 }
189             }
190 
191         } else {
192             throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode());
193         }
194 
195         return naturalLanguage.toString();
196     }
197 
198     private String getCompoundSeperator(PropositionDefinition proposition, boolean isRoot) {
199         String operator = getCompoundOperator(proposition);
200         if (isRoot){
201             return ". " + StringUtils.capitalize(operator) + " ";
202         }
203         return "; " + operator + " ";
204     }
205 
206     private String getCompoundOperator(PropositionDefinition proposition) {
207         String operator = null;
208         if (LogicalOperator.AND.getCode().equalsIgnoreCase(proposition.getCompoundOpCode())) {
209             operator = "and";
210         } else if (LogicalOperator.OR.getCode().equalsIgnoreCase(proposition.getCompoundOpCode())) {
211             operator = "or";
212         }
213         return operator;
214     }
215 
216     @Override
217     public NaturalLanguageTree translateNaturalLanguageTreeForProposition(String naturalLanguageUsageId,
218             PropositionDefinition proposition,
219             String languageCode) throws RiceIllegalArgumentException {
220         NaturalLanguageTemplate naturalLanguageTemplate = getNaturalLanguageTemplateForProposition(naturalLanguageUsageId, proposition, languageCode);
221 
222         NaturalLanguageTree.Builder tree = NaturalLanguageTree.Builder.create();
223         if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
224             Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition);
225             String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap);
226             tree.setNaturalLanguage(naturalLanguage);
227 
228         } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
229             Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(naturalLanguageUsageId, proposition, languageCode);
230             String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap);
231             tree.setNaturalLanguage(naturalLanguage);
232 
233             //Null check because newly created compound propositions should also be translateable.
234             if(proposition.getCompoundComponents()!=null){
235                 List<NaturalLanguageTree> children = new ArrayList<NaturalLanguageTree>();
236                 for (PropositionDefinition child : proposition.getCompoundComponents()) {
237                     children.add(this.translateNaturalLanguageTreeForProposition(naturalLanguageUsageId, child, languageCode));
238                 }
239                 tree.setChildren(children);
240             }
241 
242         } else {
243             throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode());
244         }
245 
246         return tree.build();
247     }
248 
249     protected NaturalLanguageTemplate getNaturalLanguageTemplateForProposition(String naturalLanguageUsageId, PropositionDefinition proposition, String languageCode) {
250         NaturalLanguageTemplate naturalLanguageTemplate = null;
251         //Continue if typeid is null, some children may not be initialized yet.
252         if (proposition.getTypeId() != null) {
253             naturalLanguageTemplate = this.ruleManagementService.findNaturalLanguageTemplateByLanguageCodeTypeIdAndNluId(languageCode,
254                     proposition.getTypeId(), naturalLanguageUsageId);
255         }
256         return naturalLanguageTemplate;
257     }
258 
259     protected Map<String, Object> buildSimplePropositionContextMap(PropositionDefinition proposition) {
260         if (!proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) {
261             throw new RiceIllegalArgumentException("proposition is not simple " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription());
262         }
263         Map<String, Object> contextMap = new LinkedHashMap<String, Object>();
264         for (PropositionParameter param : proposition.getParameters()) {
265             if (param.getParameterType().equals(PropositionParameterType.TERM.getCode())) {
266                 TermDefinition term = param.getTermValue();
267                 if ((term == null) && (StringUtils.isNotBlank(param.getValue()))) {
268                    term = this.termRepositoryService.getTerm(param.getValue());
269                 }
270                 if (term != null) {
271                     for (TermParameterDefinition termParam : term.getParameters()) {
272                         contextMap.put(termParam.getName(), termParam.getValue());
273                     }
274                 } else {
275                     contextMap.put(param.getParameterType(), param.getValue());
276                 }
277             } else {
278                 contextMap.put(param.getParameterType(), param.getValue());
279             }
280         }
281         return contextMap;
282     }
283 
284     protected Map<String, Object> buildCompoundPropositionContextMap(String naturalLanguageUsageId, PropositionDefinition proposition, String languageCode) {
285         if (!proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) {
286             throw new RiceIllegalArgumentException("proposition us not compound " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription());
287         }
288         return new LinkedHashMap<String, Object>();
289     }
290 
291 }