001 /**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krms.api.repository.proposition;
017
018 import java.io.Serializable;
019 import java.util.ArrayList;
020 import java.util.Collection;
021 import java.util.Collections;
022 import java.util.List;
023
024 import javax.xml.bind.annotation.XmlAccessType;
025 import javax.xml.bind.annotation.XmlAccessorType;
026 import javax.xml.bind.annotation.XmlAnyElement;
027 import javax.xml.bind.annotation.XmlElement;
028 import javax.xml.bind.annotation.XmlElementWrapper;
029 import javax.xml.bind.annotation.XmlRootElement;
030 import javax.xml.bind.annotation.XmlType;
031 import org.kuali.rice.krms.api.repository.term.TermParameterDefinition;
032
033 import org.apache.commons.lang.StringUtils;
034 import org.kuali.rice.core.api.CoreConstants;
035 import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
036 import org.kuali.rice.core.api.mo.ModelBuilder;
037 import org.kuali.rice.krms.api.KrmsConstants;
038 import org.kuali.rice.krms.api.repository.LogicalOperator;
039 import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
040
041 /**
042 * Concrete model object implementation of KRMS Proposition.
043 * Immutable.
044 * Instances of Proposition can be (un)marshalled to and from XML.
045 *
046 * There are three main types of Propositions:
047 * 1. Compound Propositions - a proposition consisting of other propositions
048 * and a boolean algebra operator (AND, OR) defining how to evaluate those propositions.
049 * 2. Parameterized Propositions - a proposition which is parameterized by some set of values,
050 * evaluation logic is implemented by hand and returns true or false
051 * 3. Simple Propositions - a proposition of the form lhs op rhs where
052 * lhs=left-hand side, rhs=right-hand side, and op=operator
053 * Propositions are reference by a rule or another proposition (in the case of compound propositions).
054 * Propositions are never re-used across multiple rules.
055 * Each proposition can have zero or more parameters. The proposition parameter is the primary
056 * data element used to define the proposition. (@see PropositionParameter)
057 *
058 * @see PropositonContract
059 * @see PropositionParameterContract
060 */
061 @XmlRootElement(name = PropositionDefinition.Constants.ROOT_ELEMENT_NAME)
062 @XmlAccessorType(XmlAccessType.NONE)
063 @XmlType(name = PropositionDefinition.Constants.TYPE_NAME, propOrder = {
064 PropositionDefinition.Elements.ID,
065 PropositionDefinition.Elements.DESC,
066 PropositionDefinition.Elements.RULE_ID,
067 PropositionDefinition.Elements.TYPE_ID,
068 PropositionDefinition.Elements.PROP_TYPE_CODE,
069 PropositionDefinition.Elements.PARAMETERS, // xml element name differs from class property name
070 PropositionDefinition.Elements.CMPND_OP_CODE,
071 PropositionDefinition.Elements.CMPND_COMPONENTS,
072 CoreConstants.CommonElements.VERSION_NUMBER,
073 PropositionDefinition.Elements.CMPND_SEQ_NO,
074 CoreConstants.CommonElements.FUTURE_ELEMENTS
075 })
076 public final class PropositionDefinition extends AbstractDataTransferObject implements PropositionDefinitionContract {
077 private static final long serialVersionUID = 2783959459503209577L;
078
079 // TODO: change this to field name to id
080 @XmlElement(name = Elements.ID, required=true)
081 private String id;
082
083 @XmlElement(name = Elements.DESC, required=true)
084 private String description;
085
086 @XmlElement(name = Elements.TYPE_ID, required=true)
087 private String typeId;
088
089 @XmlElement(name = Elements.RULE_ID, required=true)
090 private String ruleId;
091
092 @XmlElement(name = Elements.PROP_TYPE_CODE, required=true)
093 private String propositionTypeCode;
094
095 @XmlElementWrapper(name = Elements.PARAMETERS)
096 @XmlElement(name = Elements.PARAMETER, required=false)
097 private List<PropositionParameter> parameters;
098
099 @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 }