001/** 002 * Copyright 2005-2015 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 */ 016package org.kuali.rice.krms.api.repository.proposition; 017 018import java.io.Serializable; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.List; 023 024import javax.xml.bind.annotation.XmlAccessType; 025import javax.xml.bind.annotation.XmlAccessorType; 026import javax.xml.bind.annotation.XmlAnyElement; 027import javax.xml.bind.annotation.XmlElement; 028import javax.xml.bind.annotation.XmlElementWrapper; 029import javax.xml.bind.annotation.XmlRootElement; 030import javax.xml.bind.annotation.XmlType; 031import org.kuali.rice.krms.api.repository.term.TermParameterDefinition; 032 033import org.apache.commons.lang.StringUtils; 034import org.kuali.rice.core.api.CoreConstants; 035import org.kuali.rice.core.api.mo.AbstractDataTransferObject; 036import org.kuali.rice.core.api.mo.ModelBuilder; 037import org.kuali.rice.krms.api.KrmsConstants; 038import org.kuali.rice.krms.api.repository.LogicalOperator; 039import 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}) 076public 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}