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