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.HashMap; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.lang.StringUtils; 029import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 030import org.kuali.rice.krms.api.repository.LogicalOperator; 031import org.kuali.rice.krms.api.repository.NaturalLanguageTree; 032import org.kuali.rice.krms.api.repository.RuleManagementService; 033import org.kuali.rice.krms.api.repository.TranslateBusinessMethods; 034import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition; 035import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition; 036import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplate; 037import org.kuali.rice.krms.api.repository.language.NaturalLanguageTemplaterContract; 038import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition; 039import org.kuali.rice.krms.api.repository.proposition.PropositionParameter; 040import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType; 041import org.kuali.rice.krms.api.repository.proposition.PropositionType; 042import org.kuali.rice.krms.api.repository.rule.RuleDefinition; 043import org.kuali.rice.krms.api.repository.term.TermDefinition; 044import org.kuali.rice.krms.api.repository.term.TermParameterDefinition; 045import org.kuali.rice.krms.api.repository.term.TermRepositoryService; 046 047/** 048 * @author nwright 049 */ 050public class KSTranslationUtility implements TranslateBusinessMethods { 051 052 private RuleManagementService ruleManagementService; 053 private TermRepositoryService termRepositoryService; 054 private NaturalLanguageTemplaterContract templater; 055 056 public KSTranslationUtility(RuleManagementService ruleManagementService, TermRepositoryService termRepositoryService, 057 NaturalLanguageTemplaterContract templater) { 058 this.ruleManagementService = ruleManagementService; 059 this.termRepositoryService = termRepositoryService; 060 this.templater = templater; 061 } 062 063 public RuleManagementService getRuleManagementService() { 064 return ruleManagementService; 065 } 066 067 public void setRuleManagementService(RuleManagementService ruleManagementService) { 068 this.ruleManagementService = ruleManagementService; 069 } 070 071 public NaturalLanguageTemplaterContract getTemplater() { 072 return templater; 073 } 074 075 public void setTemplater(NaturalLanguageTemplaterContract templater) { 076 this.templater = templater; 077 } 078 079 @Override 080 public String translateNaturalLanguageForObject(String naturalLanguageUsageId, String typeId, String krmsObjectId, String languageCode) 081 throws RiceIllegalArgumentException { 082 083 Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode); 084 085 if (typeId.equals("agenda")) { 086 AgendaDefinition agenda = this.ruleManagementService.getAgenda(krmsObjectId); 087 if (agenda == null) { 088 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for an agenda"); 089 } 090 return this.translateNaturalLanguageForAgenda(agenda, templateMap); 091 } else if (typeId.equals("rule")) { 092 RuleDefinition rule = this.ruleManagementService.getRule(krmsObjectId); 093 if (rule == null) { 094 throw new RiceIllegalArgumentException(krmsObjectId + " is not an Id for a rule"); 095 } 096 return this.translateNaturalLanguageForRule(rule, templateMap); 097 } else if (typeId.equals("proposition")) { 098 PropositionDefinition proposition = this.ruleManagementService.getProposition(krmsObjectId); 099 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 * This method is added because from a functional point of view the root proposition is ignored when it is a group 170 * and therefore handled differently. 171 * 172 * @param proposition 173 * @param templateMap 174 * @param isRoot 175 * @return 176 */ 177 private String translateNaturalLanguageForProposition(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap, boolean isRoot) { 178 NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId()); 179 180 StringBuilder naturalLanguage = new StringBuilder(); 181 if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 182 if(naturalLanguageTemplate!=null){ 183 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition); 184 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap)); 185 } 186 187 } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 188 if(naturalLanguageTemplate!=null){ 189 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap); 190 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap)); 191 } 192 193 //Null check because newly created compound propositions should also be translateable. 194 if(proposition.getCompoundComponents()!=null){ 195 String operator = getCompoundSeperator(proposition, isRoot); 196 for (PropositionDefinition child : proposition.getCompoundComponents()) { 197 if(proposition.getCompoundComponents().indexOf(child)!=0){ 198 naturalLanguage.append(operator); 199 } 200 naturalLanguage.append(this.translateNaturalLanguageForProposition(child, templateMap, false)); 201 } 202 } 203 204 } else { 205 throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode()); 206 } 207 208 return naturalLanguage.toString(); 209 } 210 211 private String getCompoundSeperator(PropositionDefinition proposition, boolean isRoot) { 212 String operator = getCompoundOperator(proposition); 213 if (isRoot){ 214 return ". " + StringUtils.capitalize(operator) + " "; 215 } 216 return "; " + operator + " "; 217 } 218 219 private String getCompoundOperator(PropositionDefinition proposition) { 220 String operator = null; 221 if (LogicalOperator.AND.getCode().equalsIgnoreCase(proposition.getCompoundOpCode())) { 222 operator = "and"; 223 } else if (LogicalOperator.OR.getCode().equalsIgnoreCase(proposition.getCompoundOpCode())) { 224 operator = "or"; 225 } 226 return operator; 227 } 228 229 @Override 230 public NaturalLanguageTree translateNaturalLanguageTreeForProposition(String naturalLanguageUsageId, 231 PropositionDefinition proposition, 232 String languageCode) throws RiceIllegalArgumentException { 233 234 Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode); 235 return translateNaturalLanguageTreeForProposition(proposition, templateMap); 236 } 237 238 public NaturalLanguageTree translateNaturalLanguageTreeForProposition(PropositionDefinition proposition, 239 Map<String, NaturalLanguageTemplate> templateMap) throws RiceIllegalArgumentException { 240 241 NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId()); 242 243 NaturalLanguageTree.Builder tree = NaturalLanguageTree.Builder.create(); 244 if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 245 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition); 246 String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap); 247 tree.setNaturalLanguage(naturalLanguage); 248 249 } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 250 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap); 251 String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap); 252 tree.setNaturalLanguage(naturalLanguage); 253 254 //Null check because newly created compound propositions should also be translateable. 255 if(proposition.getCompoundComponents()!=null){ 256 List<NaturalLanguageTree> children = new ArrayList<NaturalLanguageTree>(); 257 for (PropositionDefinition child : proposition.getCompoundComponents()) { 258 children.add(this.translateNaturalLanguageTreeForProposition(child, templateMap)); 259 } 260 tree.setChildren(children); 261 } 262 263 } else { 264 throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode()); 265 } 266 267 return tree.build(); 268 } 269 270 protected Map<String, Object> buildSimplePropositionContextMap(PropositionDefinition proposition) { 271 if (!proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 272 throw new RiceIllegalArgumentException("proposition is not simple " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription()); 273 } 274 Map<String, Object> contextMap = new LinkedHashMap<String, Object>(); 275 for (PropositionParameter param : proposition.getParameters()) { 276 if (param.getParameterType().equals(PropositionParameterType.TERM.getCode())) { 277 TermDefinition term = param.getTermValue(); 278 if ((term == null) && (StringUtils.isNotBlank(param.getValue()))) { 279 term = this.termRepositoryService.getTerm(param.getValue()); 280 } 281 if (term != null) { 282 for (TermParameterDefinition termParam : term.getParameters()) { 283 contextMap.put(termParam.getName(), termParam.getValue()); 284 } 285 } else { 286 contextMap.put(param.getParameterType(), param.getValue()); 287 } 288 } else { 289 contextMap.put(param.getParameterType(), param.getValue()); 290 } 291 } 292 return contextMap; 293 } 294 295 protected Map<String, Object> buildCompoundPropositionContextMap(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap) { 296 if (!proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 297 throw new RiceIllegalArgumentException("proposition us not compound " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription()); 298 } 299 return new LinkedHashMap<String, Object>(); 300 } 301 302}