001/** 002 * Copyright 2005-2013 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 KSTranslationUtility implements TranslateBusinessMethods { 050 051 private RuleManagementService ruleManagementService; 052 private TermRepositoryService termRepositoryService; 053 private NaturalLanguageTemplaterContract templater; 054 055 public KSTranslationUtility(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 if (typeId.equals("agenda")) { 083 AgendaDefinition agenda = this.ruleManagementService.getAgenda(krmsObjectId); 084 if (agenda == null) { 085 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for an agenda"); 086 } 087 return this.translateNaturalLanguageForAgenda(naturalLanguageUsageId, agenda, languageCode); 088 } else if (typeId.equals("rule")) { 089 RuleDefinition rule = this.ruleManagementService.getRule(krmsObjectId); 090 if (rule == null) { 091 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a rule"); 092 } 093 return this.translateNaturalLanguageForRule(naturalLanguageUsageId, rule, languageCode); 094 } else if (typeId.equals("proposition")) { 095 PropositionDefinition proposition = this.ruleManagementService.getProposition(krmsObjectId); 096 if (proposition == null) { 097 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a proposition"); 098 } 099 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}