001 /** 002 * Copyright 2005-2011 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 */ 051 @XmlRootElement(name = RuleDefinition.Constants.ROOT_ELEMENT_NAME) 052 @XmlAccessorType(XmlAccessType.NONE) 053 @XmlType(name = RuleDefinition.Constants.TYPE_NAME, propOrder = { 054 RuleDefinition.Elements.ID, 055 RuleDefinition.Elements.NAME, 056 RuleDefinition.Elements.NAMESPACE, 057 RuleDefinition.Elements.DESCRIPTION, 058 RuleDefinition.Elements.TYPE_ID, 059 RuleDefinition.Elements.PROPOSITION, 060 RuleDefinition.Elements.ACTIONS, 061 RuleDefinition.Elements.ATTRIBUTES, 062 CoreConstants.CommonElements.VERSION_NUMBER, 063 CoreConstants.CommonElements.FUTURE_ELEMENTS 064 }) 065 public final class RuleDefinition extends AbstractDataTransferObject implements RuleDefinitionContract { 066 private static final long serialVersionUID = 2783959459503209577L; 067 068 @XmlElement(name = Elements.ID, required=true) 069 private final String id; 070 @XmlElement(name = Elements.NAME, required=true) 071 private final String name; 072 @XmlElement(name = Elements.NAMESPACE, required=true) 073 private final String namespace; 074 @XmlElement(name = Elements.DESCRIPTION, required=false) 075 private final String description; 076 @XmlElement(name = Elements.TYPE_ID, required=true) 077 private final String typeId; 078 @XmlElement(name = Elements.PROPOSITION, required=true) 079 private final PropositionDefinition proposition; 080 081 @XmlElementWrapper(name = Elements.ACTIONS) 082 @XmlElement(name = Elements.ACTION, required=false) 083 private final List<ActionDefinition> actions; 084 085 @XmlElement(name = Elements.ATTRIBUTES, required = false) 086 @XmlJavaTypeAdapter(value = MapStringStringAdapter.class) 087 private final Map<String, String> attributes; 088 089 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false) 090 private final Long versionNumber; 091 092 @SuppressWarnings("unused") 093 @XmlAnyElement 094 private final Collection<org.w3c.dom.Element> _futureElements = null; 095 096 @XmlTransient 097 private String propId; 098 099 /** 100 * This constructor should never be called. 101 * It is only present for use during JAXB unmarshalling. 102 */ 103 private RuleDefinition() { 104 this.id = null; 105 this.name = null; 106 this.namespace = null; 107 this.description = null; 108 this.typeId = null; 109 this.propId = null; 110 this.proposition = null; 111 this.actions = null; 112 this.attributes = null; 113 this.versionNumber = null; 114 } 115 116 /** 117 * Constructs a KRMS Repository Rule object from the given builder. 118 * This constructor is private and should only ever be invoked from the builder. 119 * 120 * @param builder the Builder from which to construct the Rule 121 */ 122 private RuleDefinition(Builder builder) { 123 this.id = builder.getId(); 124 this.name = builder.getName(); 125 this.namespace = builder.getNamespace(); 126 this.typeId = builder.getTypeId(); 127 this.propId = builder.getPropId(); 128 this.description = builder.getDescription(); 129 130 if (builder.getProposition() != null) { 131 this.proposition = builder.getProposition().build(); 132 } else { 133 this.proposition = null; 134 } 135 136 List<ActionDefinition> actionList = new ArrayList<ActionDefinition> (); 137 if (builder.getActions() != null){ 138 for (ActionDefinition.Builder b : builder.actions){ 139 actionList.add(b.build()); 140 } 141 this.actions = Collections.unmodifiableList(actionList); 142 } else { 143 this.actions = Collections.emptyList(); 144 } 145 if (builder.attributes != null){ 146 this.attributes = Collections.unmodifiableMap(builder.getAttributes()); 147 } else { 148 this.attributes = null; 149 } 150 this.versionNumber = builder.getVersionNumber(); 151 } 152 153 @Override 154 public String getId() { 155 return this.id; 156 } 157 158 @Override 159 public String getName() { 160 return this.name; 161 } 162 163 public String getDescription() { 164 return this.description; 165 } 166 167 @Override 168 public String getNamespace() { 169 return this.namespace; 170 } 171 172 @Override 173 public String getTypeId() { 174 return this.typeId; 175 } 176 177 @Override 178 public String getPropId(){ 179 return this.propId; 180 } 181 182 @Override 183 public PropositionDefinition getProposition(){ 184 return this.proposition; 185 } 186 187 @Override 188 public List<ActionDefinition> getActions(){ 189 return this.actions; 190 } 191 192 @Override 193 public Map<String, String> getAttributes() { 194 return this.attributes; 195 } 196 197 @Override 198 public Long getVersionNumber() { 199 return versionNumber; 200 } 201 202 /** 203 * This builder is used to construct instances of KRMS Repository Rule. It enforces the constraints of the {@link RuleDefinitionContract}. 204 */ 205 public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable { 206 private static final long serialVersionUID = -7850514191699945347L; 207 208 private String id; 209 private String name; 210 private String description; 211 private String namespace; 212 private String typeId; 213 private String propId; 214 private PropositionDefinition.Builder proposition; 215 private List<ActionDefinition.Builder> actions; 216 private Map<String, String> attributes; 217 private Long versionNumber; 218 219 /** 220 * Private constructor for creating a builder with all of it's required attributes. 221 */ 222 private Builder(String ruleId, String name, String namespace, String typeId, String propId) { 223 setId(ruleId); 224 setName(name); 225 setNamespace(namespace); 226 setTypeId(typeId); 227 setPropId(propId); 228 setAttributes(new HashMap<String, String>()); 229 } 230 231 public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){ 232 return new Builder(ruleId, name, namespace, typeId, propId); 233 } 234 235 /** 236 * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}. 237 * 238 * @param contract the contract from which to populate this builder 239 * @return an instance of the builder populated with data from the contract 240 */ 241 public static Builder create(RuleDefinitionContract contract) { 242 if (contract == null) { 243 throw new IllegalArgumentException("contract is null"); 244 } 245 246 List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>(); 247 if (contract.getActions() != null){ 248 for (ActionDefinitionContract actionContract : contract.getActions()){ 249 ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract); 250 actionList.add(actBuilder); 251 } 252 } 253 254 Builder builder = new Builder(contract.getId(), contract.getName(), 255 contract.getNamespace(), contract.getTypeId(), contract.getPropId()); 256 if (contract.getProposition() != null) { 257 builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition())); 258 } 259 if (contract.getAttributes() != null){ 260 builder.setAttributes(new HashMap<String, String>(contract.getAttributes())); 261 } 262 builder.setActions(actionList); 263 builder.setVersionNumber(contract.getVersionNumber()); 264 builder.setDescription(contract.getDescription()); 265 return builder; 266 } 267 268 /** 269 * Sets the value of the id on this builder to the given value. 270 * 271 * @param ruleId the id value to set, must not be null or blank 272 * @throws IllegalArgumentException if the id is null or blank 273 */ 274 275 public void setId(String ruleId) { 276 if (ruleId != null && StringUtils.isBlank(ruleId)) { 277 throw new IllegalArgumentException("rule ID must be null or else non-blank"); 278 } 279 this.id = ruleId; 280 } 281 282 public void setName(String name) { 283 if (StringUtils.isBlank(name)) { 284 throw new IllegalArgumentException("name is blank"); 285 } 286 this.name = name; 287 } 288 289 public void setDescription(String description) { 290 if (description != null && StringUtils.isBlank(description)) { 291 throw new IllegalArgumentException("description is non-null but is blank"); 292 } 293 this.description = description; 294 } 295 296 public void setNamespace(String namespace) { 297 if (StringUtils.isBlank(namespace)) { 298 throw new IllegalArgumentException("namespace is blank"); 299 } 300 this.namespace = namespace; 301 } 302 303 public void setTypeId(String typeId) { 304 this.typeId = typeId; 305 } 306 307 public void setPropId(String propId) { 308 if (propId != null && StringUtils.isBlank(propId)) { 309 throw new IllegalArgumentException("propId must be null or non-blank"); 310 } 311 this.propId = propId; 312 } 313 314 public void setProposition(PropositionDefinition.Builder prop) { 315 this.proposition = prop; 316 this.setPropId(prop.getId()); 317 } 318 319 public void setActions(List<ActionDefinition.Builder> actions) { 320 if (actions == null){ 321 this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>()); 322 return; 323 } 324 this.actions = Collections.unmodifiableList(actions); 325 } 326 327 public void setAttributes(Map<String, String> attributes){ 328 if (attributes == null){ 329 this.attributes = Collections.emptyMap(); 330 } 331 this.attributes = Collections.unmodifiableMap(attributes); 332 } 333 334 public void setVersionNumber(Long versionNumber){ 335 this.versionNumber = versionNumber; 336 } 337 338 @Override 339 public String getId() { 340 return id; 341 } 342 343 @Override 344 public String getName() { 345 return name; 346 } 347 348 @Override 349 public String getDescription() { 350 return description; 351 } 352 353 @Override 354 public String getNamespace() { 355 return namespace; 356 } 357 358 @Override 359 public String getTypeId() { 360 return typeId; 361 } 362 363 @Override 364 public String getPropId() { 365 return propId; 366 } 367 368 @Override 369 public PropositionDefinition.Builder getProposition() { 370 return proposition; 371 } 372 373 @Override 374 public List<ActionDefinition.Builder> getActions(){ 375 return actions; 376 } 377 @Override 378 public Map<String, String> getAttributes() { 379 return attributes; 380 } 381 382 @Override 383 public Long getVersionNumber() { 384 return versionNumber; 385 } 386 387 /** 388 * Builds an instance of a Rule based on the current state of the builder. 389 * 390 * @return the fully-constructed Rule 391 */ 392 @Override 393 public RuleDefinition build() { 394 return new RuleDefinition(this); 395 } 396 397 } 398 399 /** 400 * Defines some internal constants used on this class. 401 */ 402 public static class Constants { 403 final static String ROOT_ELEMENT_NAME = "rule"; 404 final static String TYPE_NAME = "RuleType"; 405 } 406 407 /** 408 * A private class which exposes constants which define the XML element names to use 409 * when this object is marshalled to XML. 410 */ 411 public static class Elements { 412 final static String ID = "id"; 413 final static String NAME = "name"; 414 final static String DESCRIPTION = "description"; 415 final static String NAMESPACE = "namespace"; 416 final static String TYPE_ID = "typeId"; 417 final static String PROPOSITION = "proposition"; 418 final static String ACTIONS = "actions"; 419 final static String ACTION = "action"; 420 final static String ATTRIBUTES = "attributes"; 421 } 422 423 }