001 /**
002 * Copyright 2005-2013 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
032 import org.apache.commons.lang.StringUtils;
033 import org.kuali.rice.core.api.CoreConstants;
034 import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
035 import org.kuali.rice.core.api.mo.ModelBuilder;
036 import org.kuali.rice.krms.api.KrmsConstants;
037 import org.kuali.rice.krms.api.repository.LogicalOperator;
038 import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
039
040 /**
041 * Concrete model object implementation of KRMS Proposition.
042 * Immutable.
043 * Instances of Proposition can be (un)marshalled to and from XML.
044 *
045 * There are three main types of Propositions:
046 * 1. Compound Propositions - a proposition consisting of other propositions
047 * and a boolean algebra operator (AND, OR) defining how to evaluate those propositions.
048 * 2. Parameterized Propositions - a proposition which is parameterized by some set of values,
049 * evaluation logic is implemented by hand and returns true or false
050 * 3. Simple Propositions - a proposition of the form lhs op rhs where
051 * lhs=left-hand side, rhs=right-hand side, and op=operator
052 * Propositions are reference by a rule or another proposition (in the case of compound propositions).
053 * Propositions are never re-used across multiple rules.
054 * Each proposition can have zero or more parameters. The proposition parameter is the primary
055 * data element used to define the proposition. (@see PropositionParameter)
056 *
057 * @see PropositonContract
058 * @see PropositionParameterContract
059 */
060 @XmlRootElement(name = PropositionDefinition.Constants.ROOT_ELEMENT_NAME)
061 @XmlAccessorType(XmlAccessType.NONE)
062 @XmlType(name = PropositionDefinition.Constants.TYPE_NAME, propOrder = {
063 PropositionDefinition.Elements.ID,
064 PropositionDefinition.Elements.DESC,
065 PropositionDefinition.Elements.RULE_ID,
066 PropositionDefinition.Elements.TYPE_ID,
067 PropositionDefinition.Elements.PROP_TYPE_CODE,
068 PropositionDefinition.Elements.PARAMETERS, // xml element name differs from class property name
069 PropositionDefinition.Elements.CMPND_OP_CODE,
070 PropositionDefinition.Elements.CMPND_COMPONENTS,
071 CoreConstants.CommonElements.VERSION_NUMBER,
072 CoreConstants.CommonElements.FUTURE_ELEMENTS
073 })
074 public final class PropositionDefinition extends AbstractDataTransferObject implements PropositionDefinitionContract {
075 private static final long serialVersionUID = 2783959459503209577L;
076
077 // TODO: change this to field name to id
078 @XmlElement(name = Elements.ID, required=true)
079 private String id;
080
081 @XmlElement(name = Elements.DESC, required=true)
082 private String description;
083
084 @XmlElement(name = Elements.TYPE_ID, required=true)
085 private String typeId;
086
087 @XmlElement(name = Elements.RULE_ID, required=true)
088 private String ruleId;
089
090 @XmlElement(name = Elements.PROP_TYPE_CODE, required=true)
091 private String propositionTypeCode;
092
093 @XmlElementWrapper(name = Elements.PARAMETERS)
094 @XmlElement(name = Elements.PARAMETER, required=false)
095 private List<PropositionParameter> parameters;
096
097 @XmlElement(name = Elements.CMPND_OP_CODE, required=false)
098 private String compoundOpCode;
099
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 }