001 package org.kuali.rice.krms.api.repository.proposition;
002
003 import java.io.Serializable;
004 import java.util.ArrayList;
005 import java.util.Collection;
006 import java.util.Collections;
007 import java.util.List;
008
009 import javax.xml.bind.annotation.XmlAccessType;
010 import javax.xml.bind.annotation.XmlAccessorType;
011 import javax.xml.bind.annotation.XmlAnyElement;
012 import javax.xml.bind.annotation.XmlElement;
013 import javax.xml.bind.annotation.XmlElementWrapper;
014 import javax.xml.bind.annotation.XmlRootElement;
015 import javax.xml.bind.annotation.XmlType;
016
017 import org.apache.commons.lang.StringUtils;
018 import org.apache.commons.lang.builder.EqualsBuilder;
019 import org.apache.commons.lang.builder.HashCodeBuilder;
020 import org.apache.commons.lang.builder.ToStringBuilder;
021 import org.kuali.rice.core.api.CoreConstants;
022 import org.kuali.rice.core.api.mo.ModelBuilder;
023 import org.kuali.rice.core.api.mo.ModelObjectComplete;
024 import org.kuali.rice.krms.api.repository.LogicalOperator;
025 import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
026 import org.kuali.rice.krms.api.repository.rule.RuleDefinition.Elements;
027
028
029 /**
030 * Concrete model object implementation of KRMS Proposition.
031 * Immutable.
032 * Instances of Proposition can be (un)marshalled to and from XML.
033 *
034 * There are three main types of Propositions:
035 * 1. Compound Propositions - a proposition consisting of other propositions
036 * and a boolean algebra operator (AND, OR) defining how to evaluate those propositions.
037 * 2. Parameterized Propositions - a proposition which is parameterized by some set of values,
038 * evaluation logic is implemented by hand and returns true or false
039 * 3. Simple Propositions - a proposition of the form lhs op rhs where
040 * lhs=left-hand side, rhs=right-hand side, and op=operator
041 * Propositions are reference by a rule or another proposition (in the case of compound propositions).
042 * Propositions are never re-used across multiple rules.
043 * Each proposition can have zero or more parameters. The proposition parameter is the primary
044 * data element used to define the proposition. (@see PropositionParameter)
045 *
046 * @see PropositonContract
047 * @see PropositionParameterContract
048 */
049 @XmlRootElement(name = PropositionDefinition.Constants.ROOT_ELEMENT_NAME)
050 @XmlAccessorType(XmlAccessType.NONE)
051 @XmlType(name = PropositionDefinition.Constants.TYPE_NAME, propOrder = {
052 PropositionDefinition.Elements.ID,
053 PropositionDefinition.Elements.DESC,
054 PropositionDefinition.Elements.RULE_ID,
055 PropositionDefinition.Elements.TYPE_ID,
056 PropositionDefinition.Elements.PROP_TYPE_CODE,
057 PropositionDefinition.Elements.PARAMETERS, // xml element name differs from class property name
058 PropositionDefinition.Elements.CMPND_OP_CODE,
059 PropositionDefinition.Elements.CMPND_COMPONENTS,
060 CoreConstants.CommonElements.VERSION_NUMBER,
061 CoreConstants.CommonElements.FUTURE_ELEMENTS
062 })
063 public final class PropositionDefinition implements PropositionDefinitionContract, ModelObjectComplete{
064 private static final long serialVersionUID = 2783959459503209577L;
065
066 // TODO: change this to field name to id
067 @XmlElement(name = Elements.ID, required=true)
068 private String id;
069
070 @XmlElement(name = Elements.DESC, required=true)
071 private String description;
072
073 @XmlElement(name = Elements.TYPE_ID, required=true)
074 private String typeId;
075
076 @XmlElement(name = Elements.RULE_ID, required=true)
077 private String ruleId;
078
079 @XmlElement(name = Elements.PROP_TYPE_CODE, required=true)
080 private String propositionTypeCode;
081
082 @XmlElementWrapper(name = Elements.PARAMETERS)
083 @XmlElement(name = Elements.PARAMETER, required=false)
084 private List<PropositionParameter> parameters;
085
086 @XmlElement(name = Elements.CMPND_OP_CODE, required=false)
087 private String compoundOpCode;
088
089 @XmlElementWrapper(name = Elements.CMPND_COMPONENTS, required=false)
090 @XmlElement(name = Elements.CMPND_COMPONENT, required=false)
091 private List<PropositionDefinition> compoundComponents;
092
093 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
094 private final Long versionNumber;
095
096 @SuppressWarnings("unused")
097 @XmlAnyElement
098 private final Collection<org.w3c.dom.Element> _futureElements = null;
099
100
101 /**
102 * This constructor should never be called. It is only present for use during JAXB unmarshalling.
103 */
104 private PropositionDefinition() {
105 this.id = null;
106 this.description = null;
107 this.typeId = null;
108 this.propositionTypeCode = null;
109 this.parameters = null;
110 this.compoundOpCode = null;
111 this.compoundComponents = null;
112 this.versionNumber = null;
113 }
114
115 /**
116 * Constructs a KRMS Proposition from the given builder.
117 * This constructor is private and should only ever be invoked from the builder.
118 *
119 * @param builder the Builder from which to construct the KRMS Proposition
120 */
121 private PropositionDefinition(Builder builder) {
122 this.id = builder.getId();
123 this.description = builder.getDescription();
124 this.ruleId = builder.getRuleId();
125 this.typeId = builder.getTypeId();
126 this.propositionTypeCode = builder.getPropositionTypeCode();
127
128 // Build parameter list
129 List<PropositionParameter> paramList = new ArrayList<PropositionParameter>();
130 for (PropositionParameter.Builder b : builder.parameters){
131 paramList.add(b.build());
132 }
133 this.parameters = Collections.unmodifiableList(paramList);
134
135 // Build Compound Proposition properties
136 this.compoundOpCode = builder.getCompoundOpCode();
137 List <PropositionDefinition> componentList = new ArrayList<PropositionDefinition>();
138 if (builder.compoundComponents != null){
139 for (PropositionDefinition.Builder b : builder.compoundComponents){
140 componentList.add(b.build());
141 }
142 this.compoundComponents = Collections.unmodifiableList(componentList);
143 }
144 this.versionNumber = builder.getVersionNumber();
145 }
146
147 @Override
148 public String getId() {
149 return this.id;
150 }
151
152 @Override
153 public String getDescription() {
154 return this.description;
155 }
156
157 /**
158 * @return the ruleId
159 */
160 public String getRuleId() {
161 return this.ruleId;
162 }
163
164 @Override
165 public String getTypeId() {
166 return this.typeId;
167 }
168
169 @Override
170 public String getPropositionTypeCode() {
171 return this.propositionTypeCode;
172 }
173
174 @Override
175 public List<PropositionParameter> getParameters() {
176 return this.parameters;
177 }
178
179 @Override
180 public String getCompoundOpCode() {
181 return this.compoundOpCode;
182 }
183
184 @Override
185 public List<PropositionDefinition> getCompoundComponents() {
186 return this.compoundComponents;
187 }
188
189 @Override
190 public Long getVersionNumber() {
191 return versionNumber;
192 }
193
194 /**
195 * This builder is used to construct instances of KRMS Proposition. It enforces the constraints of the {@link PropositionDefinitionContract}.
196 */
197 public static class Builder implements PropositionDefinitionContract, ModelBuilder, Serializable {
198 private static final long serialVersionUID = -6889320709850568900L;
199
200 private String id;
201 private String description;
202 private String ruleId;
203 private String typeId;
204 private String propositionTypeCode;
205 private List<PropositionParameter.Builder> parameters;
206 private String compoundOpCode;
207 private List<PropositionDefinition.Builder> compoundComponents;
208 private RuleDefinition.Builder rule;
209 private Long versionNumber;
210
211 /**
212 * Private constructor for creating a builder with all of it's required attributes.
213 * @param typeId TODO
214 */
215 private Builder(String propId, String propTypeCode, String ruleId, String typeId, List<PropositionParameter.Builder> parameters) {
216 setId(propId);
217 setPropositionTypeCode(propTypeCode);
218 setRuleId(ruleId);
219 setTypeId(typeId);
220 setParameters(parameters);
221 }
222
223 public Builder compoundOpCode(String opCode){
224 setCompoundOpCode(opCode);
225 return this;
226 }
227
228 public Builder compoundComponents (List<PropositionDefinition.Builder> components){
229 setCompoundComponents(components);
230 return this;
231 }
232
233 public static Builder create(String propId, String propTypeCode, String ruleId, String typeId, List<PropositionParameter.Builder> parameters){
234 return new Builder(propId, propTypeCode, ruleId, typeId, parameters);
235 }
236
237 /**
238 * Creates a builder by populating it with data from the given {@link PropositionDefinitionContract}.
239 *
240 * @param contract the contract from which to populate this builder
241 * @return an instance of the builder populated with data from the contract
242 */
243 public static Builder create(PropositionDefinitionContract contract) {
244 if (contract == null) {
245 throw new IllegalArgumentException("contract is null");
246 }
247 List <PropositionParameter.Builder> paramBuilderList = new ArrayList<PropositionParameter.Builder>();
248 if (contract.getParameters() != null){
249 for (PropositionParameterContract paramContract : contract.getParameters()){
250 PropositionParameter.Builder myBuilder = PropositionParameter.Builder.create(paramContract);
251 paramBuilderList.add(myBuilder);
252 }
253 }
254 Builder builder = new Builder(contract.getId(), contract.getPropositionTypeCode(), contract.getRuleId(), contract.getTypeId(), paramBuilderList);
255
256 List <PropositionDefinition.Builder> componentBuilderList = new ArrayList<PropositionDefinition.Builder>();
257 if (contract.getCompoundComponents() != null) {
258 for (PropositionDefinitionContract cContract : contract.getCompoundComponents()){
259 PropositionDefinition.Builder pBuilder = PropositionDefinition.Builder.create(cContract);
260 componentBuilderList.add(pBuilder);
261 }
262 builder.setCompoundComponents(componentBuilderList);
263 }
264 builder.setCompoundOpCode(contract.getCompoundOpCode());
265 builder.setDescription(contract.getDescription());
266 builder.setVersionNumber(contract.getVersionNumber());
267 return builder;
268 }
269
270 /**
271 * Sets the value of the id on this builder to the given value.
272 *
273 * @param id the id value to set
274 */
275
276 public void setId(String propId) {
277 if (propId != null && StringUtils.isBlank(propId)) {
278 throw new IllegalArgumentException("proposition id must not be blank");
279 }
280 this.id = propId;
281 }
282
283 public void setDescription(String desc) {
284 this.description = desc;
285 }
286
287 public void setTypeId(String typeId) {
288 this.typeId = typeId;
289 }
290
291 public void setRuleId(String ruleId) {
292 this.ruleId = ruleId;
293 }
294
295 public void setRule(RuleDefinition.Builder rule) {
296 if (rule != null && !StringUtils.isBlank(rule.getId())) {
297 setRuleId(rule.getId());
298 }
299 this.rule = rule;
300 }
301
302 public void setPropositionTypeCode(String propTypeCode) {
303 if (StringUtils.isBlank(propTypeCode)) {
304 throw new IllegalArgumentException("proposition type code is blank");
305 }
306 if (!PropositionType.VALID_TYPE_CODES.contains(propTypeCode)) {
307 throw new IllegalArgumentException("invalid proposition type code");
308 }
309 this.propositionTypeCode = propTypeCode;
310 }
311
312 public void setParameters(List<PropositionParameter.Builder> parameters) {
313 // compound propositions have empty parameter lists
314 // Simple propositions must have a non-empty parameter list
315 if (parameters == null || parameters.isEmpty()){
316 this.parameters = Collections.unmodifiableList(new ArrayList<PropositionParameter.Builder>());
317 } else {
318 this.parameters = Collections.unmodifiableList(parameters);
319 }
320 }
321
322 public void setCompoundOpCode(String opCode){
323 if (StringUtils.isBlank(opCode)){ return; }
324 if (!LogicalOperator.OP_CODES.contains(opCode)){
325 throw new IllegalArgumentException("invalid opCode value");
326 }
327 this.compoundOpCode = opCode;
328 }
329
330 public void setCompoundComponents(List<PropositionDefinition.Builder> components){
331 if (components == null || components.isEmpty()){
332 this.compoundComponents = Collections.unmodifiableList(new ArrayList<PropositionDefinition.Builder>());
333 return;
334 }
335 this.compoundComponents = Collections.unmodifiableList(components);
336 }
337
338 public void setVersionNumber(Long versionNumber){
339 this.versionNumber = versionNumber;
340 }
341
342 @Override
343 public String getId() {
344 return id;
345 }
346
347 @Override
348 public String getDescription() {
349 return description;
350 }
351
352 @Override
353 public String getRuleId() {
354 return ruleId;
355 }
356
357 @Override
358 public String getTypeId() {
359 return typeId;
360 }
361
362 @Override
363 public String getPropositionTypeCode() {
364 return propositionTypeCode;
365 }
366
367 @Override
368 public List<PropositionParameter.Builder> getParameters() {
369 return parameters;
370 }
371
372 @Override
373 public String getCompoundOpCode() {
374 return compoundOpCode;
375 }
376
377 @Override
378 public List<PropositionDefinition.Builder> getCompoundComponents() {
379 return compoundComponents;
380 }
381
382 @Override
383 public Long getVersionNumber() {
384 return versionNumber;
385 }
386
387 /**
388 * Builds an instance of a Proposition based on the current state of the builder.
389 *
390 * @return the fully-constructed Proposition
391 */
392 @Override
393 public PropositionDefinition build() {
394 return new PropositionDefinition(this);
395 }
396
397 }
398
399
400 @Override
401 public int hashCode() {
402 return HashCodeBuilder.reflectionHashCode(this, Constants.HASH_CODE_EQUALS_EXCLUDE);
403 }
404
405 @Override
406 public boolean equals(Object obj) {
407 return EqualsBuilder.reflectionEquals(obj, this, Constants.HASH_CODE_EQUALS_EXCLUDE);
408 }
409
410 @Override
411 public String toString() {
412 return ToStringBuilder.reflectionToString(this);
413 }
414
415 /**
416 * Defines some internal constants used on this class.
417 */
418 static class Constants {
419 final static String ROOT_ELEMENT_NAME = "proposition";
420 final static String TYPE_NAME = "PropositionType";
421 final static String[] HASH_CODE_EQUALS_EXCLUDE = { CoreConstants.CommonElements.FUTURE_ELEMENTS };
422 }
423
424 /**
425 * A private class which exposes constants which define the XML element names to use
426 * when this object is marshalled to XML.
427 */
428 public static class Elements {
429 final static String ID = "id";
430 final static String DESC = "description";
431 final static String RULE_ID = "ruleId";
432 final static String TYPE_ID = "typeId";
433 final static String PROP_TYPE_CODE = "propositionTypeCode";
434 final static String PARAMETER = "parameter";
435 final static String PARAMETERS = "parameters";
436 final static String CMPND_OP_CODE = "compoundOpCode";
437 final static String CMPND_COMPONENTS = "compoundComponents";
438 final static String CMPND_COMPONENT = "proposition";
439 }
440
441
442 }