View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.impl.ui;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.criteria.QueryByCriteria;
21  import org.kuali.rice.core.api.criteria.QueryResults;
22  import org.kuali.rice.core.api.util.ConcreteKeyValue;
23  import org.kuali.rice.core.api.util.KeyValue;
24  import org.kuali.rice.krad.service.KRADServiceLocator;
25  import org.kuali.rice.krad.uif.control.UifKeyValuesFinderBase;
26  import org.kuali.rice.krad.uif.view.ViewModel;
27  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
28  import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
29  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
30  import org.kuali.rice.krms.impl.repository.AgendaItemBo;
31  import org.kuali.rice.krms.impl.repository.CategoryBo;
32  import org.kuali.rice.krms.impl.repository.ContextValidTermBo;
33  import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
34  import org.kuali.rice.krms.impl.repository.PropositionBo;
35  import org.kuali.rice.krms.impl.repository.TermBo;
36  import org.kuali.rice.krms.impl.repository.TermResolverBo;
37  import org.kuali.rice.krms.impl.repository.TermSpecificationBo;
38  import org.kuali.rice.krms.impl.util.KrmsImplConstants;
39  
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Set;
46  
47  /**
48   * ValuesFinder used to populate the list of available Terms when creating/editing a proposition in a
49   * KRMS Rule.
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public class ValidTermsForPropositionValuesFinder extends UifKeyValuesFinderBase {
54  
55      /**
56       * get the value list for the Term dropdown in the KRMS rule editing UI
57       * @param model
58       * @return
59       */
60      @Override
61      public List<KeyValue> getKeyValues(ViewModel model) {
62          List<KeyValue> keyValues = new ArrayList<KeyValue>();
63  
64          MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
65          PropositionBo rootProposition = ((PropositionBo) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject());
66  
67          PropositionBo editModeProposition = findPropositionUnderEdit(rootProposition);
68          String selectedCategoryId = (editModeProposition != null) ? editModeProposition.getCategoryId() : null;
69  
70          // Get all valid terms
71  
72          List<ContextValidTermBo> contextValidTerms = getContextValidTerms(rootProposition.getRuleId());
73  
74          List<String> termSpecIds = new ArrayList();
75  
76          for (ContextValidTermBo validTerm : contextValidTerms) {
77              termSpecIds.add(validTerm.getTermSpecificationId());
78          }
79  
80          if (termSpecIds.size() > 0) { // if we don't have any valid terms, skip it
81              QueryResults<TermBo> terms = null;
82              Map<String,Object> critMap = new HashMap<String,Object>();
83              critMap.put("specificationId", termSpecIds);
84  
85              QueryByCriteria criteria =
86                      QueryByCriteria.Builder.forAttribute("specificationId", termSpecIds).setOrderByAscending("description").build();
87  
88              terms = KRADServiceLocator.getDataObjectService().findMatching(TermBo.class, criteria);
89  
90              // add all terms that are in the selected category (or else add 'em all if no category is selected)
91              if (!CollectionUtils.isEmpty(terms.getResults())) for (TermBo term : terms.getResults()) {
92                  String selectName = term.getDescription();
93  
94                  if (StringUtils.isBlank(selectName) || "null".equals(selectName)) {
95                      selectName = term.getSpecification().getName();
96                  }
97  
98                  if (!StringUtils.isBlank(selectedCategoryId)) {
99                      // 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 }