View Javadoc

1   package org.kuali.rice.krms.api.repository.proposition;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.Collections;
7   import java.util.List;
8   
9   import javax.xml.bind.annotation.XmlAccessType;
10  import javax.xml.bind.annotation.XmlAccessorType;
11  import javax.xml.bind.annotation.XmlAnyElement;
12  import javax.xml.bind.annotation.XmlElement;
13  import javax.xml.bind.annotation.XmlElementWrapper;
14  import javax.xml.bind.annotation.XmlRootElement;
15  import javax.xml.bind.annotation.XmlType;
16  
17  import org.apache.commons.lang.StringUtils;
18  import org.apache.commons.lang.builder.EqualsBuilder;
19  import org.apache.commons.lang.builder.HashCodeBuilder;
20  import org.apache.commons.lang.builder.ToStringBuilder;
21  import org.kuali.rice.core.api.CoreConstants;
22  import org.kuali.rice.core.api.mo.ModelBuilder;
23  import org.kuali.rice.core.api.mo.ModelObjectComplete;
24  import org.kuali.rice.krms.api.repository.LogicalOperator;
25  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
26  import org.kuali.rice.krms.api.repository.rule.RuleDefinition.Elements;
27  
28  
29  /**
30   * Concrete model object implementation of KRMS Proposition. 
31   * Immutable. 
32   * Instances of Proposition can be (un)marshalled to and from XML.
33   *
34   * There are three main types of Propositions:
35   *    1. Compound Propositions - a proposition consisting of other propositions
36   *    	 and a boolean algebra operator (AND, OR) defining how to evaluate those propositions.
37   *    2. Parameterized Propositions - a proposition which is parameterized by some set of values, 
38   *    	 evaluation logic is implemented by hand and returns true or false
39   *    3. Simple Propositions - a proposition of the form lhs op rhs where 
40   *    	lhs=left-hand side, rhs=right-hand side, and op=operator
41   * Propositions are reference by a rule or another proposition (in the case of compound propositions).
42   * Propositions are never re-used across multiple rules.
43   * Each proposition can have zero or more parameters. The proposition parameter is the primary 
44   * data element used to define the proposition.  (@see PropositionParameter)
45   * 
46   * @see PropositonContract
47   * @see PropositionParameterContract
48   */
49  @XmlRootElement(name = PropositionDefinition.Constants.ROOT_ELEMENT_NAME)
50  @XmlAccessorType(XmlAccessType.NONE)
51  @XmlType(name = PropositionDefinition.Constants.TYPE_NAME, propOrder = {
52  		PropositionDefinition.Elements.ID,
53  		PropositionDefinition.Elements.DESC,
54          PropositionDefinition.Elements.RULE_ID,
55          PropositionDefinition.Elements.TYPE_ID,
56  		PropositionDefinition.Elements.PROP_TYPE_CODE,
57  		PropositionDefinition.Elements.PARAMETERS,									// xml element name differs from class property name
58  		PropositionDefinition.Elements.CMPND_OP_CODE,
59  		PropositionDefinition.Elements.CMPND_COMPONENTS,
60          CoreConstants.CommonElements.VERSION_NUMBER,
61  		CoreConstants.CommonElements.FUTURE_ELEMENTS
62  })
63  public final class PropositionDefinition implements PropositionDefinitionContract, ModelObjectComplete{
64  	private static final long serialVersionUID = 2783959459503209577L;
65  
66  	// TODO: change this to field name to id
67  	@XmlElement(name = Elements.ID, required=true)
68  	private String id;
69  	
70  	@XmlElement(name = Elements.DESC, required=true)
71  	private String description;
72  	
73  	@XmlElement(name = Elements.TYPE_ID, required=true)
74  	private String typeId;
75  	
76      @XmlElement(name = Elements.RULE_ID, required=true)
77      private String ruleId;
78      
79      @XmlElement(name = Elements.PROP_TYPE_CODE, required=true)
80  	private String propositionTypeCode;
81  
82  	@XmlElementWrapper(name = Elements.PARAMETERS)
83  	@XmlElement(name = Elements.PARAMETER, required=false)
84  	private List<PropositionParameter> parameters;
85  	
86  	@XmlElement(name = Elements.CMPND_OP_CODE, required=false)
87  	private String compoundOpCode;
88  	
89  	@XmlElementWrapper(name = Elements.CMPND_COMPONENTS, required=false)
90  	@XmlElement(name = Elements.CMPND_COMPONENT, required=false)
91  	private List<PropositionDefinition> compoundComponents;
92  	
93      @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
94      private final Long versionNumber;
95      	
96  	@SuppressWarnings("unused")
97      @XmlAnyElement
98      private final Collection<org.w3c.dom.Element> _futureElements = null;
99  	
100 	
101 	 /** 
102      * This constructor should never be called.  It is only present for use during JAXB unmarshalling. 
103      */
104     private PropositionDefinition() {
105     	this.id = null;
106     	this.description = null;
107     	this.typeId = null;
108     	this.propositionTypeCode = null;
109     	this.parameters = null;
110     	this.compoundOpCode = null;
111     	this.compoundComponents = null;
112         this.versionNumber = null;
113     }
114     
115     /**
116 	 * Constructs a KRMS Proposition from the given builder.  
117 	 * This constructor is private and should only ever be invoked from the builder.
118 	 * 
119 	 * @param builder the Builder from which to construct the KRMS Proposition
120 	 */
121     private PropositionDefinition(Builder builder) {
122         this.id = builder.getId();
123         this.description = builder.getDescription();
124         this.ruleId = builder.getRuleId();
125         this.typeId = builder.getTypeId();
126         this.propositionTypeCode = builder.getPropositionTypeCode();
127         
128         // Build parameter list
129         List<PropositionParameter> paramList = new ArrayList<PropositionParameter>();
130         for (PropositionParameter.Builder b : builder.parameters){
131         	paramList.add(b.build());
132         }
133         this.parameters = Collections.unmodifiableList(paramList);
134         
135         // Build Compound Proposition properties
136         this.compoundOpCode = builder.getCompoundOpCode();
137         List <PropositionDefinition> componentList = new ArrayList<PropositionDefinition>();
138         if (builder.compoundComponents != null){
139         	for (PropositionDefinition.Builder b : builder.compoundComponents){
140         		componentList.add(b.build());
141         	}
142             this.compoundComponents = Collections.unmodifiableList(componentList);
143         }
144         this.versionNumber = builder.getVersionNumber();
145     }
146     
147 	@Override
148 	public String getId() {
149 		return this.id;
150 	}
151 
152 	@Override
153 	public String getDescription() {
154 		return this.description;
155 	}
156 
157     /**
158      * @return the ruleId
159      */
160     public String getRuleId() {
161         return this.ruleId;
162     }
163 
164 	@Override
165 	public String getTypeId() {
166 		return this.typeId;
167 	}
168 
169 	@Override
170 	public String getPropositionTypeCode() {
171 		return this.propositionTypeCode; 
172 	}
173 
174 	@Override
175 	public List<PropositionParameter> getParameters() {
176 		return this.parameters; 
177 	}
178 
179 	@Override
180 	public String getCompoundOpCode() {
181 		return this.compoundOpCode; 
182 	}
183 
184 	@Override
185 	public List<PropositionDefinition> getCompoundComponents() {
186 		return this.compoundComponents; 
187 	}
188 
189     @Override
190     public Long getVersionNumber() {
191         return versionNumber;
192     }
193         
194 	/**
195      * This builder is used to construct instances of KRMS Proposition.  It enforces the constraints of the {@link PropositionDefinitionContract}.
196      */
197     public static class Builder implements PropositionDefinitionContract, ModelBuilder, Serializable {
198     	private static final long serialVersionUID = -6889320709850568900L;
199 		
200         private String id;
201         private String description;
202         private String ruleId;
203         private String typeId;
204         private String propositionTypeCode;
205         private List<PropositionParameter.Builder> parameters;
206         private String compoundOpCode;
207         private List<PropositionDefinition.Builder> compoundComponents;
208         private RuleDefinition.Builder rule;
209         private Long versionNumber;
210 
211 		/**
212 		 * Private constructor for creating a builder with all of it's required attributes.
213 		 * @param typeId TODO
214 		 */
215         private Builder(String propId, String propTypeCode, String ruleId, String typeId, List<PropositionParameter.Builder> parameters) {
216         	setId(propId);
217 			setPropositionTypeCode(propTypeCode);
218 			setRuleId(ruleId);
219 			setTypeId(typeId);
220 			setParameters(parameters);
221         }
222         
223         public Builder compoundOpCode(String opCode){
224         	setCompoundOpCode(opCode);
225         	return this;
226         }
227         
228         public Builder compoundComponents (List<PropositionDefinition.Builder> components){
229         	setCompoundComponents(components);
230         	return this;
231         }
232  
233         public static Builder create(String propId, String propTypeCode, String ruleId, String typeId, List<PropositionParameter.Builder> parameters){
234         	return new Builder(propId, propTypeCode, ruleId, typeId, parameters);
235         }
236         
237         /**
238          * Creates a builder by populating it with data from the given {@link PropositionDefinitionContract}.
239          * 
240          * @param contract the contract from which to populate this builder
241          * @return an instance of the builder populated with data from the contract
242          */
243         public static Builder create(PropositionDefinitionContract contract) {
244         	if (contract == null) {
245                 throw new IllegalArgumentException("contract is null");
246             }
247         	List <PropositionParameter.Builder> paramBuilderList = new ArrayList<PropositionParameter.Builder>();
248         	if (contract.getParameters() != null){
249         		for (PropositionParameterContract paramContract : contract.getParameters()){
250         			PropositionParameter.Builder myBuilder = PropositionParameter.Builder.create(paramContract);
251         			paramBuilderList.add(myBuilder);
252         		}
253         	}
254             Builder builder =  new Builder(contract.getId(), contract.getPropositionTypeCode(), contract.getRuleId(), contract.getTypeId(), paramBuilderList);
255             
256         	List <PropositionDefinition.Builder> componentBuilderList = new ArrayList<PropositionDefinition.Builder>();
257         	if (contract.getCompoundComponents() != null) {
258         		for (PropositionDefinitionContract cContract : contract.getCompoundComponents()){
259         			PropositionDefinition.Builder pBuilder = PropositionDefinition.Builder.create(cContract);
260         			componentBuilderList.add(pBuilder);
261         		}
262                 builder.setCompoundComponents(componentBuilderList);
263         	}
264         	builder.setCompoundOpCode(contract.getCompoundOpCode());
265             builder.setDescription(contract.getDescription());
266             builder.setVersionNumber(contract.getVersionNumber());
267             return builder;
268         }
269 
270 		/**
271 		 * Sets the value of the id on this builder to the given value.
272 		 * 
273 		 * @param id the id value to set
274 		 */
275 
276         public void setId(String propId) {
277             if (propId != null && StringUtils.isBlank(propId)) {
278                 throw new IllegalArgumentException("proposition id must not be blank");            	
279             }
280 			this.id = propId;
281 		}
282 
283 		public void setDescription(String desc) {
284 			this.description = desc;
285 		}
286 		
287         public void setTypeId(String typeId) {
288             this.typeId = typeId;
289         }
290         
291         public void setRuleId(String ruleId) {
292             this.ruleId = ruleId;
293         }
294         
295         public void setRule(RuleDefinition.Builder rule) {
296             if (rule != null && !StringUtils.isBlank(rule.getId())) {
297                 setRuleId(rule.getId());
298             }
299             this.rule = rule;
300         }
301         
302 		public void setPropositionTypeCode(String propTypeCode) {
303 			if (StringUtils.isBlank(propTypeCode)) {
304                 throw new IllegalArgumentException("proposition type code is blank");
305 			}
306 			if (!PropositionType.VALID_TYPE_CODES.contains(propTypeCode)) {
307                 throw new IllegalArgumentException("invalid proposition type code");
308 			}
309 			this.propositionTypeCode = propTypeCode;
310 		}
311 		
312 		public void setParameters(List<PropositionParameter.Builder> parameters) {
313 			// compound propositions have empty parameter lists
314 			// Simple propositions must have a non-empty parameter list
315 			if (parameters == null || parameters.isEmpty()){
316 				this.parameters = Collections.unmodifiableList(new ArrayList<PropositionParameter.Builder>());
317 			} else {
318 			    this.parameters = Collections.unmodifiableList(parameters);
319 			}
320 		}
321 		
322 		public void setCompoundOpCode(String opCode){
323 			if (StringUtils.isBlank(opCode)){ return; }
324 			if (!LogicalOperator.OP_CODES.contains(opCode)){
325 				throw new IllegalArgumentException("invalid opCode value");
326 			}
327 			this.compoundOpCode = opCode;
328 		}
329 
330 		public void setCompoundComponents(List<PropositionDefinition.Builder> components){
331 			if (components == null || components.isEmpty()){
332 				this.compoundComponents = Collections.unmodifiableList(new ArrayList<PropositionDefinition.Builder>());
333 				return;
334 			}
335 			this.compoundComponents = Collections.unmodifiableList(components);
336 		}
337 		
338         public void setVersionNumber(Long versionNumber){
339             this.versionNumber = versionNumber;
340         }
341         
342 		@Override
343 		public String getId() {
344 			return id;
345 		}
346 
347 		@Override
348 		public String getDescription() {
349 			return description;
350 		}
351 		
352 		@Override
353 		public String getRuleId() {
354 		    return ruleId;
355 		}
356 
357 		@Override
358 		public String getTypeId() {
359 			return typeId;
360 		}
361 
362 		@Override
363 		public String getPropositionTypeCode() {
364 			return propositionTypeCode;
365 		}
366 		
367 		@Override
368 		public List<PropositionParameter.Builder> getParameters() {
369 			return parameters;
370 		}
371 
372 		@Override
373 		public String getCompoundOpCode() {
374 			return compoundOpCode;
375 		}
376 		
377 		@Override
378 		public List<PropositionDefinition.Builder> getCompoundComponents() {
379 			return compoundComponents;
380 		}
381 
382         @Override
383         public Long getVersionNumber() {
384             return versionNumber;
385         }
386 
387 		/**
388 		 * Builds an instance of a Proposition based on the current state of the builder.
389 		 * 
390 		 * @return the fully-constructed Proposition
391 		 */
392         @Override
393         public PropositionDefinition build() {
394             return new PropositionDefinition(this);
395         }
396 		
397     }
398     
399     
400 	@Override
401 	public int hashCode() {
402 		return HashCodeBuilder.reflectionHashCode(this, Constants.HASH_CODE_EQUALS_EXCLUDE);
403 	}
404 
405 	@Override
406 	public boolean equals(Object obj) {
407 		return EqualsBuilder.reflectionEquals(obj, this, Constants.HASH_CODE_EQUALS_EXCLUDE);
408 	}
409 
410 	@Override
411 	public String toString() {
412 		return ToStringBuilder.reflectionToString(this);
413 	}
414 	
415 	/**
416 	 * Defines some internal constants used on this class.
417 	 */
418 	static class Constants {
419 		final static String ROOT_ELEMENT_NAME = "proposition";
420 		final static String TYPE_NAME = "PropositionType";
421 		final static String[] HASH_CODE_EQUALS_EXCLUDE = { CoreConstants.CommonElements.FUTURE_ELEMENTS };
422 	}
423 	
424 	/**
425 	 * A private class which exposes constants which define the XML element names to use
426 	 * when this object is marshalled to XML.
427 	 */
428 	public static class Elements {
429 		final static String ID = "id";
430 		final static String DESC = "description";
431         final static String RULE_ID = "ruleId";
432 		final static String TYPE_ID = "typeId";
433 		final static String PROP_TYPE_CODE = "propositionTypeCode";
434 		final static String PARAMETER = "parameter";
435 		final static String PARAMETERS = "parameters";
436 		final static String CMPND_OP_CODE = "compoundOpCode";
437 		final static String CMPND_COMPONENTS = "compoundComponents";
438 		final static String CMPND_COMPONENT = "proposition";
439 	}
440 
441 	
442 }