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 @Override 205 public Map<String, String> getAttributes() { 206 return this.attributes; 207 } 208 209 @Override 210 public Long getVersionNumber() { 211 return versionNumber; 212 } 213 214 /** 215 * This builder is used to construct instances of KRMS Repository Rule. It enforces the constraints of the {@link RuleDefinitionContract}. 216 */ 217 public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable { 218 private static final long serialVersionUID = -7850514191699945347L; 219 220 private String id; 221 private String name; 222 private String description; 223 private String namespace; 224 private String typeId; 225 private String propId; 226 private boolean active; 227 private PropositionDefinition.Builder proposition; 228 private List<ActionDefinition.Builder> actions; 229 private Map<String, String> attributes; 230 private Long versionNumber; 231 232 /** 233 * Private constructor for creating a builder with all of it's required attributes. 234 */ 235 private Builder(String ruleId, String name, String namespace, String typeId, String propId) { 236 setId(ruleId); 237 setName(name); 238 setNamespace(namespace); 239 setTypeId(typeId); 240 setPropId(propId); 241 setActive(true); 242 setAttributes(new HashMap<String, String>()); 243 } 244 245 /** 246 * Create a builder with the given parameters. 247 * 248 * @param ruleId 249 * @param name 250 * @param namespace 251 * @param typeId 252 * @param propId 253 * @return Builder 254 */ 255 public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){ 256 return new Builder(ruleId, name, namespace, typeId, propId); 257 } 258 259 /** 260 * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}. 261 * 262 * @param contract the contract from which to populate this builder 263 * @return an instance of the builder populated with data from the contract 264 */ 265 public static Builder create(RuleDefinitionContract contract) { 266 if (contract == null) { 267 throw new IllegalArgumentException("contract is null"); 268 } 269 270 List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>(); 271 if (contract.getActions() != null){ 272 for (ActionDefinitionContract actionContract : contract.getActions()){ 273 ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract); 274 actionList.add(actBuilder); 275 } 276 } 277 278 Builder builder = new Builder(contract.getId(), contract.getName(), 279 contract.getNamespace(), contract.getTypeId(), contract.getPropId()); 280 if (contract.getProposition() != null) { 281 builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition())); 282 } 283 if (contract.getAttributes() != null){ 284 builder.setAttributes(new HashMap<String, String>(contract.getAttributes())); 285 } 286 builder.setActions(actionList); 287 builder.setVersionNumber(contract.getVersionNumber()); 288 builder.setDescription(contract.getDescription()); 289 builder.setActive(contract.isActive()); 290 return builder; 291 } 292 293 /** 294 * Sets the value of the id on this builder to the given value. 295 * 296 * @param ruleId the id value to set, must not be null or blank 297 * @throws IllegalArgumentException if the id is null or blank 298 */ 299 300 public void setId(String ruleId) { 301 if (ruleId != null && StringUtils.isBlank(ruleId)) { 302 throw new IllegalArgumentException("rule ID must be null or else non-blank"); 303 } 304 this.id = ruleId; 305 } 306 307 /** 308 * Sets the value of the name on this builder to the given value 309 * @param name 310 * @throws IllegalArgumentException if the name is null or blank 311 */ 312 public void setName(String name) { 313 if (StringUtils.isBlank(name)) { 314 throw new IllegalArgumentException("name is blank"); 315 } 316 this.name = name; 317 } 318 319 /** 320 * Sets the value of the description on this builder to the given value 321 * @param description 322 */ 323 public void setDescription(String description) { 324 this.description = description; 325 } 326 327 /** 328 * Sets the value of the namespace on this builder to the given value 329 * @param namespace 330 * @throws IllegalArgumentException if the namespace is null or blank 331 */ 332 public void setNamespace(String namespace) { 333 if (StringUtils.isBlank(namespace)) { 334 throw new IllegalArgumentException("namespace is blank"); 335 } 336 this.namespace = namespace; 337 } 338 339 /** 340 * Sets the value of the typeId on this builder to the given value 341 * @param typeId 342 */ 343 public void setTypeId(String typeId) { 344 this.typeId = typeId; 345 } 346 347 /** 348 * Sets the value of the active on this builder to the given value 349 * @param active 350 */ 351 public void setActive(boolean active) { 352 this.active = active; 353 } 354 355 /** 356 * Sets the value of the propId on this builder to the given value 357 * @param propId 358 * @throws IllegalArgumentException if the propId is null or blank 359 */ 360 public void setPropId(String propId) { 361 if (propId != null && StringUtils.isBlank(propId)) { 362 throw new IllegalArgumentException("propId must be null or non-blank"); 363 } 364 this.propId = propId; 365 } 366 367 /** 368 * Sets the value of the proposition on this builder to the given value 369 * @param prop 370 */ 371 public void setProposition(PropositionDefinition.Builder prop) { 372 this.proposition = prop; 373 this.setPropId(prop.getId()); 374 } 375 376 /** 377 * Sets the value of the actions on this builder to the given value 378 * @param actions 379 */ 380 public void setActions(List<ActionDefinition.Builder> actions) { 381 if (actions == null){ 382 this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>()); 383 return; 384 } 385 this.actions = Collections.unmodifiableList(actions); 386 } 387 388 /** 389 * Sets the value of the attributes on this builder to the given value 390 * @param attributes 391 */ 392 public void setAttributes(Map<String, String> attributes){ 393 if (attributes == null){ 394 this.attributes = Collections.emptyMap(); 395 } 396 this.attributes = Collections.unmodifiableMap(attributes); 397 } 398 399 /** 400 * Sets the value of the versionNumber on this builder to the given value 401 * @param versionNumber 402 */ 403 public void setVersionNumber(Long versionNumber){ 404 this.versionNumber = versionNumber; 405 } 406 407 @Override 408 public String getId() { 409 return id; 410 } 411 412 @Override 413 public String getName() { 414 return name; 415 } 416 417 @Override 418 public String getDescription() { 419 return description; 420 } 421 422 @Override 423 public String getNamespace() { 424 return namespace; 425 } 426 427 @Override 428 public String getTypeId() { 429 return typeId; 430 } 431 432 @Override 433 public String getPropId() { 434 return propId; 435 } 436 437 @Override 438 public boolean isActive() { 439 return active; 440 } 441 442 @Override 443 public PropositionDefinition.Builder getProposition() { 444 return proposition; 445 } 446 447 @Override 448 public List<ActionDefinition.Builder> getActions(){ 449 return actions; 450 } 451 @Override 452 public Map<String, String> getAttributes() { 453 return attributes; 454 } 455 456 @Override 457 public Long getVersionNumber() { 458 return versionNumber; 459 } 460 461 /** 462 * Builds an instance of a Rule based on the current state of the builder. 463 * 464 * @return the fully-constructed Rule 465 */ 466 @Override 467 public RuleDefinition build() { 468 return new RuleDefinition(this); 469 } 470 471 } 472 473 /** 474 * Defines some internal constants used on this class. 475 */ 476 public static class Constants { 477 final static String ROOT_ELEMENT_NAME = "rule"; 478 final static String TYPE_NAME = "RuleType"; 479 } 480 481 /** 482 * A private class which exposes constants which define the XML element names to use 483 * when this object is marshalled to XML. 484 */ 485 public static class Elements { 486 final static String ID = "id"; 487 final static String NAME = "name"; 488 final static String DESCRIPTION = "description"; 489 final static String NAMESPACE = "namespace"; 490 final static String TYPE_ID = "typeId"; 491 final static String PROPOSITION = "proposition"; 492 final static String ACTIONS = "actions"; 493 final static String ACTION = "action"; 494 final static String ACTIVE = "active"; 495 final static String ATTRIBUTES = "attributes"; 496 } 497 498 }