001/** 002 * Copyright 2005-2015 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016/* 017 * To change this template, choose Tools | Templates 018 * and open the template in the editor. 019 */ 020package org.kuali.rice.krms.impl.repository; 021 022import java.util.ArrayList; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.lang.StringUtils; 028import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 029import org.kuali.rice.krms.api.repository.LogicalOperator; 030import org.kuali.rice.krms.api.repository.NaturalLanguageTree; 031import org.kuali.rice.krms.api.repository.RuleManagementService; 032import org.kuali.rice.krms.api.repository.TranslateBusinessMethods; 033import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition; 034import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition; 035import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplate; 036import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplaterContract; 037import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition; 038import org.kuali.rice.krms.api.repository.proposition.PropositionParameter; 039import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType; 040import org.kuali.rice.krms.api.repository.proposition.PropositionType; 041import org.kuali.rice.krms.api.repository.rule.RuleDefinition; 042import org.kuali.rice.krms.api.repository.term.TermDefinition; 043import org.kuali.rice.krms.api.repository.term.TermParameterDefinition; 044import org.kuali.rice.krms.api.repository.term.TermRepositoryService; 045 046/** 047 * @author nwright 048 */ 049public class TranslationUtility implements TranslateBusinessMethods { 050 051 private RuleManagementService ruleManagementService; 052 private TermRepositoryService termRepositoryService; 053 private NaturalLanguageTemplaterContract templater; 054 055 public TranslationUtility(RuleManagementService ruleManagementService, TermRepositoryService termRepositoryService, 056 NaturalLanguageTemplaterContract templater) { 057 this.ruleManagementService = ruleManagementService; 058 this.termRepositoryService = termRepositoryService; 059 this.templater = templater; 060 } 061 062 public RuleManagementService getRuleManagementService() { 063 return ruleManagementService; 064 } 065 066 public void setRuleManagementService(RuleManagementService ruleManagementService) { 067 this.ruleManagementService = ruleManagementService; 068 } 069 070 public NaturalLanguageTemplaterContract getTemplater() { 071 return templater; 072 } 073 074 public void setTemplater(NaturalLanguageTemplaterContract templater) { 075 this.templater = templater; 076 } 077 078 @Override 079 public String translateNaturalLanguageForObject(String naturalLanguageUsageId, String typeId, String krmsObjectId, String languageCode) 080 throws RiceIllegalArgumentException { 081 082 // TODO: find out what RICE intended for this typeId? Was it supposed to be the Simple Class name? 083 if (typeId.equals("agenda")) { 084 AgendaDefinition agenda = this.ruleManagementService.getAgenda(krmsObjectId); 085 if (agenda == null) { 086 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for an agenda"); 087 } 088 return this.translateNaturalLanguageForAgenda(naturalLanguageUsageId, agenda, languageCode); 089 } else if (typeId.equals("rule")) { 090 RuleDefinition rule = this.ruleManagementService.getRule(krmsObjectId); 091 if (rule == null) { 092 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a rule"); 093 } 094 return this.translateNaturalLanguageForRule(naturalLanguageUsageId, rule, languageCode); 095 } else if (typeId.equals("proposition")) { 096 PropositionDefinition proposition = this.ruleManagementService.getProposition(krmsObjectId); 097 if (proposition == null) { 098 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a proposition"); 099 } 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}