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 * These constants declared here for Rice if they would like to move them 170 * to a constants class of their choice. Need to be able to add a 171 * blank template in the DB. Rice validations counter it. 172 */ 173 private final String KRMS_NL_TEMP_BLANK = "kuali.krms.nl.template.blank"; 174 private final String KRMS_NL_TEMP_ATTR_OPERATOR = "kuali.krms.nl.template.attribute.operator"; 175 176 /** 177 * This method is added because from a functional point of view the root proposition is ignored when it is a group 178 * and therefore handled differently. 179 * 180 * @param proposition 181 * @param templateMap 182 * @param isRoot 183 * @return 184 */ 185 private String translateNaturalLanguageForProposition(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap, boolean isRoot) { 186 NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId()); 187 188 StringBuilder naturalLanguage = new StringBuilder(); 189 if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 190 if(naturalLanguageTemplate!=null){ 191 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition); 192 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap)); 193 } 194 195 } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 196 197 if(naturalLanguageTemplate!=null && !naturalLanguageTemplate.getTemplate().equals(KRMS_NL_TEMP_BLANK)){ 198 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap); 199 naturalLanguage.append(templater.translate(naturalLanguageTemplate, contextMap)); 200 } 201 202 //Null check because newly created compound propositions should also be translateable. 203 if(proposition.getCompoundComponents()!=null){ 204 /* 205 Take note when working in this part of the method AND only for Final Matrix Exam. The same idea is carried out in 206 FERuleViewHelperServiceImpl.getDescriptionForPropositionTree 207 ln: 218 but that method is mainly used with data that is in memory whereas this method makes DB call. 208 */ 209 String operator = getCompoundSeperator(naturalLanguageTemplate, isRoot); 210 211 for (PropositionDefinition child : proposition.getCompoundComponents()) { 212 if(proposition.getCompoundComponents().indexOf(child)!=0){ 213 naturalLanguage.append(operator); 214 } 215 naturalLanguage.append(this.translateNaturalLanguageForProposition(child, templateMap, false)); 216 } 217 } 218 219 } else { 220 throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode()); 221 } 222 223 return naturalLanguage.toString(); 224 } 225 226 private String getCompoundSeperator(NaturalLanguageTemplate naturalLanguageTemplate, boolean isRoot) { 227 String operator = naturalLanguageTemplate.getAttributes().get(KRMS_NL_TEMP_ATTR_OPERATOR); 228 if (isRoot){ 229 return ". " + operator + " "; 230 } 231 return "; " + operator + " "; 232 } 233 234 235 @Override 236 public NaturalLanguageTree translateNaturalLanguageTreeForProposition(String naturalLanguageUsageId, 237 PropositionDefinition proposition, 238 String languageCode) throws RiceIllegalArgumentException { 239 240 Map<String, NaturalLanguageTemplate> templateMap = getNaturalLanguageTemplateMap(naturalLanguageUsageId, languageCode); 241 return translateNaturalLanguageTreeForProposition(proposition, templateMap); 242 } 243 244 public NaturalLanguageTree translateNaturalLanguageTreeForProposition(PropositionDefinition proposition, 245 Map<String, NaturalLanguageTemplate> templateMap) throws RiceIllegalArgumentException { 246 247 NaturalLanguageTemplate naturalLanguageTemplate = templateMap.get(proposition.getTypeId()); 248 249 NaturalLanguageTree.Builder tree = NaturalLanguageTree.Builder.create(); 250 if (proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 251 Map<String, Object> contextMap = this.buildSimplePropositionContextMap(proposition); 252 String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap); 253 tree.setNaturalLanguage(naturalLanguage); 254 255 } else if (proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 256 Map<String, Object> contextMap = this.buildCompoundPropositionContextMap(proposition, templateMap); 257 String naturalLanguage = templater.translate(naturalLanguageTemplate, contextMap); 258 tree.setNaturalLanguage(naturalLanguage); 259 260 //Null check because newly created compound propositions should also be translateable. 261 if(proposition.getCompoundComponents()!=null){ 262 List<NaturalLanguageTree> children = new ArrayList<NaturalLanguageTree>(); 263 for (PropositionDefinition child : proposition.getCompoundComponents()) { 264 children.add(this.translateNaturalLanguageTreeForProposition(child, templateMap)); 265 } 266 tree.setChildren(children); 267 } 268 269 } else { 270 throw new RiceIllegalArgumentException("Unknown proposition type: " + proposition.getPropositionTypeCode()); 271 } 272 273 return tree.build(); 274 } 275 276 protected Map<String, Object> buildSimplePropositionContextMap(PropositionDefinition proposition) { 277 if (!proposition.getPropositionTypeCode().equals(PropositionType.SIMPLE.getCode())) { 278 throw new RiceIllegalArgumentException("proposition is not simple " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription()); 279 } 280 Map<String, Object> contextMap = new LinkedHashMap<String, Object>(); 281 for (PropositionParameter param : proposition.getParameters()) { 282 if (param.getParameterType().equals(PropositionParameterType.TERM.getCode())) { 283 TermDefinition term = param.getTermValue(); 284 if ((term == null) && (StringUtils.isNotBlank(param.getValue()))) { 285 term = this.termRepositoryService.getTerm(param.getValue()); 286 } 287 if (term != null) { 288 for (TermParameterDefinition termParam : term.getParameters()) { 289 contextMap.put(termParam.getName(), termParam.getValue()); 290 } 291 } else { 292 contextMap.put(param.getParameterType(), param.getValue()); 293 } 294 } else { 295 contextMap.put(param.getParameterType(), param.getValue()); 296 } 297 } 298 return contextMap; 299 } 300 301 protected Map<String, Object> buildCompoundPropositionContextMap(PropositionDefinition proposition, Map<String, NaturalLanguageTemplate> templateMap) { 302 if (!proposition.getPropositionTypeCode().equals(PropositionType.COMPOUND.getCode())) { 303 throw new RiceIllegalArgumentException("proposition us not compound " + proposition.getPropositionTypeCode() + " " + proposition.getId() + proposition.getDescription()); 304 } 305 return new LinkedHashMap<String, Object>(); 306 } 307 308}