View Javadoc

1   /**
2    * Copyright 2005-2012 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.repository;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.criteria.CriteriaLookupService;
21  import org.kuali.rice.core.api.criteria.GenericQueryResults;
22  import org.kuali.rice.core.api.criteria.Predicate;
23  import org.kuali.rice.core.api.criteria.QueryByCriteria;
24  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
25  import org.kuali.rice.krad.service.BusinessObjectService;
26  import org.kuali.rice.krad.service.KRADServiceLocator;
27  import org.kuali.rice.krms.api.repository.RuleRepositoryService;
28  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
29  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeRuleEntry;
30  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeSubAgendaEntry;
31  import org.kuali.rice.krms.api.repository.context.ContextDefinition;
32  import org.kuali.rice.krms.api.repository.context.ContextSelectionCriteria;
33  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
34  import org.kuali.rice.krms.impl.util.KrmsImplConstants.PropertyNames;
35  
36  import java.util.ArrayList;
37  import java.util.Collections;
38  import java.util.List;
39  import java.util.Map;
40  
41  import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
42  
43  /**
44   *
45   */
46  public class RuleRepositoryServiceImpl implements RuleRepositoryService {
47      protected BusinessObjectService businessObjectService;
48      private CriteriaLookupService criteriaLookupService;
49  	
50  	/**
51  	 * This overridden method ...
52  	 * 
53  	 * @see org.kuali.rice.krms.api.repository.RuleRepositoryService#selectContext(org.kuali.rice.krms.api.repository.context.ContextSelectionCriteria)
54  	 */
55      @Override
56      public ContextDefinition selectContext(
57      		ContextSelectionCriteria contextSelectionCriteria) {
58      	if (contextSelectionCriteria == null){
59      		throw new RiceIllegalArgumentException("selection criteria is null");
60      	}
61      	if (StringUtils.isBlank(contextSelectionCriteria.getNamespaceCode())){
62      		throw new RiceIllegalArgumentException("selection criteria namespaceCode is null or blank");
63      	}
64      	QueryByCriteria queryCriteria = buildQuery(contextSelectionCriteria);
65          GenericQueryResults<ContextBo> results = getCriteriaLookupService().lookup(ContextBo.class, queryCriteria);
66  
67      	List<ContextBo> resultBos = results.getResults();
68  
69      	//assuming 1 ?
70      	ContextDefinition result = null;
71      	if (!CollectionUtils.isEmpty(resultBos)) {
72      		if (resultBos.size() == 1) {
73      			ContextBo bo = resultBos.iterator().next();
74      			return ContextBo.to(bo);
75      		}
76      		else throw new RiceIllegalArgumentException("Ambiguous context qualifiers, can not select more than one context.");
77      	}
78      	return result;
79      }
80  
81  	@Override
82  	public AgendaTreeDefinition getAgendaTree(String agendaId) {
83  		if (StringUtils.isBlank(agendaId)){
84      		throw new RiceIllegalArgumentException("agenda id is null or blank");
85      	}
86  		// Get agenda items from db, then build up agenda tree structure
87  		AgendaBo agendaBo = getBusinessObjectService().findBySinglePrimaryKey(AgendaBo.class, agendaId);
88  		String agendaItemId = agendaBo.getFirstItemId();
89  		
90  		// walk thru the agenda items, building an agenda tree definition Builder along the way
91  		AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
92  		myBuilder.setAgendaId( agendaId );
93          if (agendaItemId != null) {
94  		    myBuilder = walkAgendaItemTree(agendaItemId, myBuilder);
95          }
96  		
97  		// build the agenda tree and return it
98  		return myBuilder.build();
99  	}
100     	
101 	@Override
102 	public List<AgendaTreeDefinition> getAgendaTrees(List<String> agendaIds) {
103 		List<AgendaTreeDefinition> agendaTrees = new ArrayList<AgendaTreeDefinition>();
104 		for (String agendaId : agendaIds){
105 			agendaTrees.add( getAgendaTree(agendaId) );
106 		}
107         return Collections.unmodifiableList(agendaTrees);		
108 	}
109 	
110 	@Override
111 	public RuleDefinition getRule(String ruleId) {
112 		if (StringUtils.isBlank(ruleId)){
113 			return null;			
114 		}
115 		RuleBo bo = getBusinessObjectService().findBySinglePrimaryKey(RuleBo.class, ruleId);
116 		return RuleBo.to(bo);
117 	}
118 	
119 	@Override
120 	public List<RuleDefinition> getRules(List<String> ruleIds) {
121         if (ruleIds == null) throw new RiceIllegalArgumentException("ruleIds must not be null");
122 
123         // Fetch BOs
124         List<RuleBo> bos = null;
125         if (ruleIds.size() == 0) {
126             bos = Collections.emptyList();
127         } else {
128             QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();
129             List<Predicate> pList = new ArrayList<Predicate>();
130             qBuilder.setPredicates(in("id", ruleIds.toArray()));
131             GenericQueryResults<RuleBo> results = getCriteriaLookupService().lookup(RuleBo.class, qBuilder.build());
132 
133     	    bos = results.getResults();
134     	}
135 
136         // Translate BOs
137         ArrayList<RuleDefinition> rules = new ArrayList<RuleDefinition>();
138         for (RuleBo bo : bos) {
139             RuleDefinition rule = RuleBo.to(bo);
140             rules.add(rule);
141         }
142         return Collections.unmodifiableList(rules);
143 	}
144 
145 	/**
146 	 * Recursive method to create AgendaTreeDefinition builder
147 	 * 	
148 	 *  
149 	 */
150 	private AgendaTreeDefinition.Builder walkAgendaItemTree(String agendaItemId, AgendaTreeDefinition.Builder builder){
151 		//TODO: prevent circular, endless looping
152 		if (StringUtils.isBlank(agendaItemId)){
153 			return null;
154 		}
155 		// Get AgendaItemDefinition Business Object from database
156 		// NOTE: if we read agendaItem one at a time from db.   Could consider linking in OJB and getting all at once
157 		AgendaItemBo agendaItemBo = getBusinessObjectService().findBySinglePrimaryKey(AgendaItemBo.class, agendaItemId);
158 		
159 		// If Rule  
160 		// TODO: validate that only either rule or subagenda, not both
161 		if (!StringUtils.isBlank( agendaItemBo.getRuleId() )){
162 			// setup new rule entry builder
163 			AgendaTreeRuleEntry.Builder ruleEntryBuilder = AgendaTreeRuleEntry.Builder
164 					.create(agendaItemBo.getId(), agendaItemBo.getRuleId());
165 			ruleEntryBuilder.setRuleId( agendaItemBo.getRuleId() );
166 			ruleEntryBuilder.setAgendaItemId( agendaItemBo.getId() );
167 			if (agendaItemBo.getWhenTrueId() != null){
168 				// Go follow the true branch, creating AgendaTreeDefinintion Builder for the
169 				// true branch level
170 				AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
171 				myBuilder.setAgendaId( agendaItemBo.getAgendaId() );
172 				ruleEntryBuilder.setIfTrue( walkAgendaItemTree(agendaItemBo.getWhenTrueId(),myBuilder));
173 			}
174 			if (agendaItemBo.getWhenFalseId() != null){
175 				// Go follow the false branch, creating AgendaTreeDefinintion Builder 
176 				AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
177 				myBuilder.setAgendaId( agendaItemBo.getAgendaId() );
178 				ruleEntryBuilder.setIfFalse( walkAgendaItemTree(agendaItemBo.getWhenFalseId(), myBuilder));
179 			}
180 			// Build the Rule Entry and add it to the AgendaTreeDefinition builder
181 			builder.addRuleEntry( ruleEntryBuilder.build() );
182 		}
183 		// if SubAgenda and a sub agenda tree entry
184 		if (!StringUtils.isBlank(agendaItemBo.getSubAgendaId())) {
185 			AgendaTreeSubAgendaEntry.Builder subAgendaEntryBuilder = 
186 				AgendaTreeSubAgendaEntry.Builder.create(agendaItemBo.getId(), agendaItemBo.getSubAgendaId());
187 			builder.addSubAgendaEntry( subAgendaEntryBuilder.build() );
188 			}
189 
190 		// if this agenda item has an "After Id", follow that branch
191 		if (!StringUtils.isBlank( agendaItemBo.getAlwaysId() )){
192 			builder = walkAgendaItemTree( agendaItemBo.getAlwaysId(), builder);
193 			
194 		}
195 		return builder;
196 	}
197 	
198 	/**
199 	 * 
200 	 * This method converts a {@link ContextSelectionCriteria} object into a
201 	 * {@link QueryByCriteria} object with the proper predicates for AttributeBo properties.
202 	 * 
203 	 * @param selectionCriteria
204 	 * @return 
205 	 */
206 	private QueryByCriteria buildQuery( ContextSelectionCriteria selectionCriteria ){
207 		Predicate p;
208 		QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();
209     	List<Predicate> pList = new ArrayList<Predicate>();
210     	if (selectionCriteria.getNamespaceCode() != null){
211     		p = equal(PropertyNames.Context.NAMESPACE, selectionCriteria.getNamespaceCode());
212     		pList.add(p);
213     	}
214     	if (selectionCriteria.getName() != null){
215     		p = equal(PropertyNames.Context.NAME, selectionCriteria.getName());
216     		pList.add(p);
217     	}
218     	if (selectionCriteria.getContextQualifiers() != null){
219     		for (Map.Entry<String, String> entry : selectionCriteria.getContextQualifiers().entrySet()){
220     			p = and(equal(PropertyNames.Context.ATTRIBUTE_BOS
221     					+ "." + PropertyNames.BaseAttribute.ATTRIBUTE_DEFINITION
222     					+ "." + PropertyNames.KrmsAttributeDefinition.NAME, entry.getKey()),
223     				equal(PropertyNames.Context.ATTRIBUTE_BOS
224     					+ "." + PropertyNames.BaseAttribute.VALUE, entry.getValue()));
225     			pList.add(p);
226     		}
227     	}
228      	Predicate[] preds = new Predicate[pList.size()];
229      	pList.toArray(preds);
230     	qBuilder.setPredicates(and(preds)); 
231 		return qBuilder.build();
232 	}
233 
234 	/**
235      * Sets the businessObjectService property.
236      *
237      * @param businessObjectService The businessObjectService to set.
238      */
239     public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
240         this.businessObjectService = businessObjectService;
241     }
242 
243     protected BusinessObjectService getBusinessObjectService() {
244 		if ( businessObjectService == null ) {
245             // TODO: inject this instead
246 			businessObjectService = KRADServiceLocator.getBusinessObjectService();
247 		}
248 		return businessObjectService;
249 	}
250     
251     /**
252      * Sets the criteriaLookupService attribute value.
253      *
254      * @param criteriaLookupService The criteriaLookupService to set.
255      */
256     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
257         this.criteriaLookupService = criteriaLookupService;
258     }
259 
260     protected CriteriaLookupService getCriteriaLookupService() {
261         return criteriaLookupService;
262     }
263     
264 }