View Javadoc

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