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