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}