View Javadoc

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