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}