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