001/**
002 * Copyright 2005-2017 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 */
016package org.kuali.rice.krms.impl.ui;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.criteria.QueryByCriteria;
021import org.kuali.rice.core.api.criteria.QueryResults;
022import org.kuali.rice.core.api.util.ConcreteKeyValue;
023import org.kuali.rice.core.api.util.KeyValue;
024import org.kuali.rice.krad.service.KRADServiceLocator;
025import org.kuali.rice.krad.uif.control.UifKeyValuesFinderBase;
026import org.kuali.rice.krad.uif.view.ViewModel;
027import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
028import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
029import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
030import org.kuali.rice.krms.impl.repository.AgendaItemBo;
031import org.kuali.rice.krms.impl.repository.CategoryBo;
032import org.kuali.rice.krms.impl.repository.ContextValidTermBo;
033import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
034import org.kuali.rice.krms.impl.repository.PropositionBo;
035import org.kuali.rice.krms.impl.repository.TermBo;
036import org.kuali.rice.krms.impl.repository.TermResolverBo;
037import org.kuali.rice.krms.impl.repository.TermSpecificationBo;
038import org.kuali.rice.krms.impl.util.KrmsImplConstants;
039
040import java.util.ArrayList;
041import java.util.HashMap;
042import java.util.HashSet;
043import java.util.List;
044import java.util.Map;
045import java.util.Set;
046
047/**
048 * ValuesFinder used to populate the list of available Terms when creating/editing a proposition in a
049 * KRMS Rule.
050 *
051 * @author Kuali Rice Team (rice.collab@kuali.org)
052 */
053public class ValidTermsForPropositionValuesFinder extends UifKeyValuesFinderBase {
054
055    /**
056     * get the value list for the Term dropdown in the KRMS rule editing UI
057     * @param model
058     * @return
059     */
060    @Override
061    public List<KeyValue> getKeyValues(ViewModel model) {
062        List<KeyValue> keyValues = new ArrayList<KeyValue>();
063
064        MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
065        PropositionBo rootProposition = ((PropositionBo) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject());
066
067        PropositionBo editModeProposition = findPropositionUnderEdit(rootProposition);
068        String selectedCategoryId = (editModeProposition != null) ? editModeProposition.getCategoryId() : null;
069
070        // Get all valid terms
071
072        List<ContextValidTermBo> contextValidTerms = getContextValidTerms(rootProposition.getRuleId());
073
074        List<String> termSpecIds = new ArrayList();
075
076        for (ContextValidTermBo validTerm : contextValidTerms) {
077            termSpecIds.add(validTerm.getTermSpecificationId());
078        }
079
080        if (termSpecIds.size() > 0) { // if we don't have any valid terms, skip it
081            QueryResults<TermBo> terms = null;
082            Map<String,Object> critMap = new HashMap<String,Object>();
083            critMap.put("specificationId", termSpecIds);
084
085            QueryByCriteria criteria =
086                    QueryByCriteria.Builder.forAttribute("specificationId", termSpecIds).setOrderByAscending("description").build();
087
088            terms = KRADServiceLocator.getDataObjectService().findMatching(TermBo.class, criteria);
089
090            // add all terms that are in the selected category (or else add 'em all if no category is selected)
091            if (!CollectionUtils.isEmpty(terms.getResults())) for (TermBo term : terms.getResults()) {
092                String selectName = term.getDescription();
093
094                if (StringUtils.isBlank(selectName) || "null".equals(selectName)) {
095                    selectName = term.getSpecification().getName();
096                }
097
098                if (!StringUtils.isBlank(selectedCategoryId)) {
099                    // only add if the term has the selected category
100                    if (isTermSpecificationInCategory(term.getSpecification(), selectedCategoryId)) {
101                        keyValues.add(new ConcreteKeyValue(term.getId(), selectName));
102                    }
103                } else {
104                    keyValues.add(new ConcreteKeyValue(term.getId(), selectName));
105                }
106            }
107
108            //
109            // Add Parameterized Term Specs
110            //
111
112            // get term resolvers for the given term specs
113            QueryByCriteria.Builder termResolverCritBuilder = QueryByCriteria.Builder.forAttribute("outputId", termSpecIds);
114            termResolverCritBuilder.setOrderByAscending("name");
115            QueryResults<TermResolverBo> termResolvers =
116                    KRADServiceLocator.getDataObjectService().findMatching(TermResolverBo.class, termResolverCritBuilder.build());
117
118            // TODO: what if there is more than one resolver for a given term specification?
119
120            if (termResolvers.getResults() != null) for (TermResolverBo termResolver : termResolvers.getResults()) {
121                if (!CollectionUtils.isEmpty(termResolver.getParameterSpecifications())) {
122                    TermSpecificationBo output = termResolver.getOutput();
123
124                    // filter by category
125                    if (StringUtils.isBlank(selectedCategoryId) ||
126                            isTermSpecificationInCategory(output, selectedCategoryId)) {
127
128                        // we use a special prefix to differentiate these, as they are term spec ids instead of term ids.
129                        keyValues.add(new ConcreteKeyValue(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX
130                                + output.getId(), output.getName()
131                                // build a string that indicates the number of parameters
132                                + "(" + StringUtils.repeat("_", ",", termResolver.getParameterSpecifications().size()) +")"));
133                    }
134                }
135            }
136        }
137
138        return keyValues;
139    }
140
141    /**
142     * Get all of the valid terms for the Context that we're in.  This is a bit of a process since we're starting
143     * from the proposition and there is a lot of indirection to get the context ID.
144     *
145     * @param ruleId
146     * @return the mappings from the context(s) to the valid terms
147     */
148    private List<ContextValidTermBo> getContextValidTerms(String ruleId) {
149        RuleDefinition rule = KrmsRepositoryServiceLocator
150            .getRuleBoService().getRuleByRuleId(ruleId);
151
152        QueryByCriteria agendaItemCriteria = QueryByCriteria.Builder.forAttribute("ruleId", rule.getId()).build();
153        QueryResults<AgendaItemBo> agendaItems =
154                KRADServiceLocator.getDataObjectService().findMatching(AgendaItemBo.class, agendaItemCriteria);
155
156        Set<String> agendaIds = new HashSet<String>();
157        if (!CollectionUtils.isEmpty(agendaItems.getResults())) for (AgendaItemBo agendaItem : agendaItems.getResults()) {
158            agendaIds.add(agendaItem.getAgendaId());
159        }
160
161        Set<String> contextIds = new HashSet<String>();
162        for (String agendaId : agendaIds) {
163            AgendaDefinition agenda = KrmsRepositoryServiceLocator.getAgendaBoService().getAgendaByAgendaId(agendaId);
164
165            if (agenda != null) {
166                contextIds.add(agenda.getContextId());
167            }
168        }
169
170        List<ContextValidTermBo> contextValidTerms = new ArrayList<ContextValidTermBo>();
171
172        for (String contextId : contextIds) {
173            QueryResults<ContextValidTermBo> queryResults =
174                    KRADServiceLocator.getDataObjectService().findMatching(ContextValidTermBo.class,
175                            QueryByCriteria.Builder.forAttribute("contextId", contextId).build());
176
177            if (!CollectionUtils.isEmpty(queryResults.getResults())) {
178                contextValidTerms.addAll(queryResults.getResults());
179            }
180        }
181        return contextValidTerms;
182    }
183
184    /**
185     * @return true if the term specification is in the given category
186     */
187    private boolean isTermSpecificationInCategory(TermSpecificationBo termSpec, String categoryId) {
188        if (termSpec.getCategories() != null) {
189            for (CategoryBo category : termSpec.getCategories()) {
190                if (categoryId.equals(category.getId())) {
191                    return true;
192                }
193            }
194        }
195        return false;
196    }
197
198    /**
199     * helper method to find the proposition under edit
200     */
201    private PropositionBo findPropositionUnderEdit(PropositionBo currentProposition) {
202        PropositionBo result = null;
203        if (currentProposition.getEditMode()) {
204            result = currentProposition;
205        } else {
206            if (currentProposition.getCompoundComponents() != null) {
207                for (PropositionBo child : currentProposition.getCompoundComponents()) {
208                    result = findPropositionUnderEdit(child);
209                    if (result != null) break;
210                }
211            }
212        }
213        return result;
214    }
215
216}