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.rule; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.core.api.CoreConstants; 020 import org.kuali.rice.core.api.mo.AbstractDataTransferObject; 021 import org.kuali.rice.core.api.mo.ModelBuilder; 022 import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter; 023 import org.kuali.rice.krms.api.KrmsConstants; 024 import org.kuali.rice.krms.api.repository.action.ActionDefinition; 025 import org.kuali.rice.krms.api.repository.action.ActionDefinitionContract; 026 import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition; 027 028 import javax.xml.bind.annotation.XmlAccessType; 029 import javax.xml.bind.annotation.XmlAccessorType; 030 import javax.xml.bind.annotation.XmlAnyElement; 031 import javax.xml.bind.annotation.XmlElement; 032 import javax.xml.bind.annotation.XmlElementWrapper; 033 import javax.xml.bind.annotation.XmlRootElement; 034 import javax.xml.bind.annotation.XmlTransient; 035 import javax.xml.bind.annotation.XmlType; 036 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 037 import java.io.Serializable; 038 import java.util.ArrayList; 039 import java.util.Collection; 040 import java.util.Collections; 041 import java.util.HashMap; 042 import java.util.List; 043 import java.util.Map; 044 045 /** 046 * Concrete model object implementation of KRMS Repository Rule 047 * immutable. 048 * Instances of Rule can be (un)marshalled to and from XML. 049 * 050 * @see RuleDefinitionContract 051 * @see org.kuali.rice.krms.framework.engine.Rule 052 */ 053 @XmlRootElement(name = RuleDefinition.Constants.ROOT_ELEMENT_NAME) 054 @XmlAccessorType(XmlAccessType.NONE) 055 @XmlType(name = RuleDefinition.Constants.TYPE_NAME, propOrder = { 056 RuleDefinition.Elements.ID, 057 RuleDefinition.Elements.NAME, 058 RuleDefinition.Elements.NAMESPACE, 059 RuleDefinition.Elements.DESCRIPTION, 060 RuleDefinition.Elements.TYPE_ID, 061 RuleDefinition.Elements.ACTIVE, 062 RuleDefinition.Elements.PROPOSITION, 063 RuleDefinition.Elements.ACTIONS, 064 RuleDefinition.Elements.ATTRIBUTES, 065 CoreConstants.CommonElements.VERSION_NUMBER, 066 CoreConstants.CommonElements.FUTURE_ELEMENTS 067 }) 068 public final class RuleDefinition extends AbstractDataTransferObject implements RuleDefinitionContract { 069 private static final long serialVersionUID = 2783959459503209577L; 070 071 @XmlElement(name = Elements.ID, required=true) 072 private final String id; 073 @XmlElement(name = Elements.NAME, required=true) 074 private final String name; 075 @XmlElement(name = Elements.NAMESPACE, required=true) 076 private final String namespace; 077 @XmlElement(name = Elements.DESCRIPTION, required=false) 078 private final String description; 079 @XmlElement(name = Elements.TYPE_ID, required=true) 080 private final String typeId; 081 @XmlElement(name = Elements.PROPOSITION, required=true) 082 private final PropositionDefinition proposition; 083 @XmlElement(name = Elements.ACTIVE, required = false) 084 private final boolean active; 085 086 @XmlElementWrapper(name = Elements.ACTIONS) 087 @XmlElement(name = Elements.ACTION, required=false) 088 private final List<ActionDefinition> actions; 089 090 @XmlElement(name = Elements.ATTRIBUTES, required = false) 091 @XmlJavaTypeAdapter(value = MapStringStringAdapter.class) 092 private final Map<String, String> attributes; 093 094 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false) 095 private final Long versionNumber; 096 097 @SuppressWarnings("unused") 098 @XmlAnyElement 099 private final Collection<org.w3c.dom.Element> _futureElements = null; 100 101 @XmlTransient 102 private String propId; 103 104 /** 105 * This constructor should never be called. 106 * It is only present for use during JAXB unmarshalling. 107 */ 108 private RuleDefinition() { 109 this.id = null; 110 this.name = null; 111 this.namespace = null; 112 this.description = null; 113 this.typeId = null; 114 this.propId = null; 115 this.active = true; 116 this.proposition = null; 117 this.actions = null; 118 this.attributes = null; 119 this.versionNumber = null; 120 } 121 122 /** 123 * Constructs a KRMS Repository Rule object from the given builder. 124 * This constructor is private and should only ever be invoked from the builder. 125 * 126 * @param builder the Builder from which to construct the Rule 127 */ 128 private RuleDefinition(Builder builder) { 129 this.id = builder.getId(); 130 this.name = builder.getName(); 131 this.namespace = builder.getNamespace(); 132 this.typeId = builder.getTypeId(); 133 this.propId = builder.getPropId(); 134 this.description = builder.getDescription(); 135 this.active = builder.isActive(); 136 137 if (builder.getProposition() != null) { 138 this.proposition = builder.getProposition().build(); 139 } else { 140 this.proposition = null; 141 } 142 143 List<ActionDefinition> actionList = new ArrayList<ActionDefinition> (); 144 if (builder.getActions() != null){ 145 for (ActionDefinition.Builder b : builder.actions){ 146 actionList.add(b.build()); 147 } 148 this.actions = Collections.unmodifiableList(actionList); 149 } else { 150 this.actions = Collections.emptyList(); 151 } 152 if (builder.attributes != null){ 153 this.attributes = Collections.unmodifiableMap(builder.getAttributes()); 154 } else { 155 this.attributes = null; 156 } 157 this.versionNumber = builder.getVersionNumber(); 158 } 159 160 @Override 161 public String getId() { 162 return this.id; 163 } 164 165 @Override 166 public String getName() { 167 return this.name; 168 } 169 170 @Override 171 public String getDescription() { 172 return this.description; 173 } 174 175 @Override 176 public String getNamespace() { 177 return this.namespace; 178 } 179 180 @Override 181 public String getTypeId() { 182 return this.typeId; 183 } 184 185 @Override 186 public String getPropId(){ 187 return this.propId; 188 } 189 190 @Override 191 public boolean isActive() { 192 return this.active; 193 } 194 195 @Override 196 public PropositionDefinition getProposition(){ 197 return this.proposition; 198 } 199 200 @Override 201 public List<ActionDefinition> getActions(){ 202 return this.actions; 203 } 204 205 /** 206 * Returns the internal representation of the set of attributes associated with the 207 * Action. The attributes are represented as name/value pairs. 208 * 209 * @return internal representation of the set of ActionAttribute objects. 210 */ 211 @Override 212 public Map<String, String> getAttributes() { 213 return this.attributes; 214 } 215 216 @Override 217 public Long getVersionNumber() { 218 return versionNumber; 219 } 220 221 /** 222 * This builder is used to construct instances of KRMS Repository Rule. It enforces the constraints of the {@link RuleDefinitionContract}. 223 */ 224 public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable { 225 private static final long serialVersionUID = -7850514191699945347L; 226 227 private String id; 228 private String name; 229 private String description; 230 private String namespace; 231 private String typeId; 232 private String propId; 233 private boolean active; 234 private PropositionDefinition.Builder proposition; 235 private List<ActionDefinition.Builder> actions; 236 private Map<String, String> attributes; 237 private Long versionNumber; 238 239 /** 240 * Private constructor for creating a builder with all of it's required attributes. 241 * 242 * @param ruleId the id value to set, must not be null or blank 243 * @param name the name value to set, must not be null or blank 244 * @param namespace the namespace value to set, must not be null or blank 245 * @param typeId the typeId value to set 246 * @param propId the propId value to set, must not be null or blank 247 */ 248 private Builder(String ruleId, String name, String namespace, String typeId, String propId) { 249 setId(ruleId); 250 setName(name); 251 setNamespace(namespace); 252 setTypeId(typeId); 253 setPropId(propId); 254 setActive(true); 255 setAttributes(new HashMap<String, String>()); 256 } 257 258 /** 259 * Create a builder with the given parameters. 260 * 261 * @param ruleId the id value to set, must not be null or blank 262 * @param name the name value to set, must not be null or blank 263 * @param namespace the namespace value to set, must not be null or blank 264 * @param typeId the typeId value to set 265 * @param propId the propId value to set, must not be null or blank 266 * @return Builder with the given values set 267 */ 268 public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){ 269 return new Builder(ruleId, name, namespace, typeId, propId); 270 } 271 272 /** 273 * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}. 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(RuleDefinitionContract contract) { 279 if (contract == null) { 280 throw new IllegalArgumentException("contract is null"); 281 } 282 283 List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>(); 284 if (contract.getActions() != null){ 285 for (ActionDefinitionContract actionContract : contract.getActions()){ 286 ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract); 287 actionList.add(actBuilder); 288 } 289 } 290 291 Builder builder = new Builder(contract.getId(), contract.getName(), 292 contract.getNamespace(), contract.getTypeId(), contract.getPropId()); 293 if (contract.getProposition() != null) { 294 builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition())); 295 } 296 if (contract.getAttributes() != null){ 297 builder.setAttributes(new HashMap<String, String>(contract.getAttributes())); 298 } 299 builder.setActions(actionList); 300 builder.setVersionNumber(contract.getVersionNumber()); 301 builder.setDescription(contract.getDescription()); 302 builder.setActive(contract.isActive()); 303 return builder; 304 } 305 306 /** 307 * Sets the value of the id on this builder to the given value. 308 * 309 * @param ruleId the id value to set, must not be null or blank 310 * @throws IllegalArgumentException if the id is null or blank 311 */ 312 313 public void setId(String ruleId) { 314 if (ruleId != null && StringUtils.isBlank(ruleId)) { 315 throw new IllegalArgumentException("rule ID must be null or else non-blank"); 316 } 317 this.id = ruleId; 318 } 319 320 /** 321 * Sets the value of the name on this builder to the given value 322 * @param name the name value to set, must not be null or blank 323 * @throws IllegalArgumentException if the name is null or blank 324 */ 325 public void setName(String name) { 326 if (StringUtils.isBlank(name)) { 327 throw new IllegalArgumentException("name is blank"); 328 } 329 this.name = name; 330 } 331 332 /** 333 * Sets the value of the description on this builder to the given value 334 * @param description 335 */ 336 public void setDescription(String description) { 337 this.description = description; 338 } 339 340 /** 341 * Sets the value of the namespace on this builder to the given value 342 * @param namespace the namespace value to set, must not be null or blank 343 * @throws IllegalArgumentException if the namespace is null or blank 344 */ 345 public void setNamespace(String namespace) { 346 if (StringUtils.isBlank(namespace)) { 347 throw new IllegalArgumentException("namespace is blank"); 348 } 349 this.namespace = namespace; 350 } 351 352 /** 353 * Sets the value of the typeId on this builder to the given value 354 * @param typeId the typeId value to set 355 */ 356 public void setTypeId(String typeId) { 357 this.typeId = typeId; 358 } 359 360 /** 361 * Sets the value of the active on this builder to the given value 362 * @param active the active value to set 363 */ 364 public void setActive(boolean active) { 365 this.active = active; 366 } 367 368 /** 369 * Sets the value of the propId on this builder to the given value 370 * @param propId the propId value to set, must not be null or blank 371 * @throws IllegalArgumentException if the propId is null or blank 372 */ 373 public void setPropId(String propId) { 374 if (propId != null && StringUtils.isBlank(propId)) { 375 throw new IllegalArgumentException("propId must be null or non-blank"); 376 } 377 this.propId = propId; 378 } 379 380 /** 381 * Sets the value of the proposition on this builder to the given value 382 * @param prop the proposition value to set, must not be null 383 */ 384 public void setProposition(PropositionDefinition.Builder prop) { 385 this.proposition = prop; 386 this.setPropId(prop.getId()); 387 } 388 389 /** 390 * Sets the value of the actions on this builder to the given value 391 * @param actions the actions value to set, can be null 392 */ 393 public void setActions(List<ActionDefinition.Builder> actions) { 394 if (actions == null){ 395 this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>()); 396 return; 397 } 398 this.actions = Collections.unmodifiableList(actions); 399 } 400 401 /** 402 * Sets the value of the attributes on this builder to the given value 403 * @param attributes the attributes values to set, can be null 404 */ 405 public void setAttributes(Map<String, String> attributes){ 406 if (attributes == null){ 407 this.attributes = Collections.emptyMap(); 408 } 409 this.attributes = Collections.unmodifiableMap(attributes); 410 } 411 412 /** 413 * Sets the value of the versionNumber on this builder to the given value 414 * @param versionNumber the versionNumber value to set 415 */ 416 public void setVersionNumber(Long versionNumber){ 417 this.versionNumber = versionNumber; 418 } 419 420 @Override 421 public String getId() { 422 return id; 423 } 424 425 @Override 426 public String getName() { 427 return name; 428 } 429 430 @Override 431 public String getDescription() { 432 return description; 433 } 434 435 @Override 436 public String getNamespace() { 437 return namespace; 438 } 439 440 @Override 441 public String getTypeId() { 442 return typeId; 443 } 444 445 @Override 446 public String getPropId() { 447 return propId; 448 } 449 450 @Override 451 public boolean isActive() { 452 return active; 453 } 454 455 @Override 456 public PropositionDefinition.Builder getProposition() { 457 return proposition; 458 } 459 460 @Override 461 public List<ActionDefinition.Builder> getActions(){ 462 return actions; 463 } 464 @Override 465 public Map<String, String> getAttributes() { 466 return attributes; 467 } 468 469 @Override 470 public Long getVersionNumber() { 471 return versionNumber; 472 } 473 474 /** 475 * Builds an instance of a Rule based on the current state of the builder. 476 * 477 * @return the fully-constructed Rule 478 */ 479 @Override 480 public RuleDefinition build() { 481 return new RuleDefinition(this); 482 } 483 484 } 485 486 /** 487 * Defines some internal constants used on this class. 488 */ 489 public static class Constants { 490 final static String ROOT_ELEMENT_NAME = "rule"; 491 final static String TYPE_NAME = "RuleType"; 492 } 493 494 /** 495 * A private class which exposes constants which define the XML element names to use 496 * when this object is marshalled to XML. 497 */ 498 public static class Elements { 499 final static String ID = "id"; 500 final static String NAME = "name"; 501 final static String DESCRIPTION = "description"; 502 final static String NAMESPACE = "namespace"; 503 final static String TYPE_ID = "typeId"; 504 final static String PROPOSITION = "proposition"; 505 final static String ACTIONS = "actions"; 506 final static String ACTION = "action"; 507 final static String ACTIVE = "active"; 508 final static String ATTRIBUTES = "attributes"; 509 } 510 511 public static class Cache { 512 public static final String NAME = KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0 + "/" + RuleDefinition.Constants.TYPE_NAME; 513 } 514 }