View Javadoc

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