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.context; 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.agenda.AgendaDefinition; 024 import org.kuali.rice.krms.api.repository.agenda.AgendaDefinitionContract; 025 026 import javax.xml.bind.annotation.XmlAccessType; 027 import javax.xml.bind.annotation.XmlAccessorType; 028 import javax.xml.bind.annotation.XmlAnyElement; 029 import javax.xml.bind.annotation.XmlElement; 030 import javax.xml.bind.annotation.XmlElementWrapper; 031 import javax.xml.bind.annotation.XmlRootElement; 032 import javax.xml.bind.annotation.XmlType; 033 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 034 import java.io.Serializable; 035 import java.util.ArrayList; 036 import java.util.Collection; 037 import java.util.Collections; 038 import java.util.HashMap; 039 import java.util.List; 040 import java.util.Map; 041 042 /** 043 * An immutable representation of a context definition. A context definition 044 * defines information about a context which can be loaded into the rules 045 * engine for evaluation. 046 * 047 * A context definition includes a list of agendas which are valid within the 048 * context. Typically, during rule engine execution, one or more of these 049 * agendas is selected for execution based on a given set of selection criteria. 050 * 051 * @see ContextDefinitionContract 052 * @author Kuali Rice Team (rice.collab@kuali.org) 053 * 054 */ 055 @XmlRootElement(name = ContextDefinition.Constants.ROOT_ELEMENT_NAME) 056 @XmlAccessorType(XmlAccessType.NONE) 057 @XmlType(name = ContextDefinition.Constants.TYPE_NAME, propOrder = { 058 ContextDefinition.Elements.ID, 059 ContextDefinition.Elements.NAMESPACE, 060 ContextDefinition.Elements.NAME, 061 ContextDefinition.Elements.TYPE_ID, 062 ContextDefinition.Elements.DESCRIPTION, 063 ContextDefinition.Elements.ACTIVE, 064 ContextDefinition.Elements.AGENDAS, 065 ContextDefinition.Elements.ATTRIBUTES, 066 CoreConstants.CommonElements.VERSION_NUMBER, 067 CoreConstants.CommonElements.FUTURE_ELEMENTS 068 }) 069 public final class ContextDefinition extends AbstractDataTransferObject implements ContextDefinitionContract { 070 071 private static final long serialVersionUID = -6639428234851623868L; 072 073 @XmlElement(name = Elements.ID, required = false) 074 private final String id; 075 076 @XmlElement(name = Elements.NAME, required = true) 077 private final String name; 078 079 @XmlElement(name = Elements.NAMESPACE, required = true) 080 private final String namespace; 081 082 @XmlElement(name = Elements.TYPE_ID, required = false) 083 private final String typeId; 084 085 @XmlElement(name = Elements.DESCRIPTION, required = false) 086 private final String description; 087 088 @XmlElement(name = Elements.ACTIVE, required = false) 089 private final boolean active; 090 091 @XmlElementWrapper(name = Elements.AGENDAS) 092 @XmlElement(name = Elements.AGENDA, required = false) 093 private final List<AgendaDefinition> agendas; 094 095 @XmlElement(name = Elements.ATTRIBUTES, required = false) 096 @XmlJavaTypeAdapter(value = MapStringStringAdapter.class) 097 private final Map<String, String> attributes; 098 099 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false) 100 private final Long versionNumber; 101 102 @SuppressWarnings("unused") 103 @XmlAnyElement 104 private final Collection<org.w3c.dom.Element> _futureElements = null; 105 106 /** 107 * Used only by JAXB. 108 */ 109 private ContextDefinition() { 110 this.id = null; 111 this.name = null; 112 this.namespace = null; 113 this.typeId = null; 114 this.description = null; 115 this.active = true; 116 this.agendas = null; 117 this.versionNumber = null; 118 this.attributes = null; 119 } 120 121 /** 122 * Constructs a ContextDefinition from the given builder. This constructor is private 123 * and should only be called by the builder 124 * 125 * @param builder the Builder from which to construct the context definition. 126 */ 127 private ContextDefinition(Builder builder) { 128 this.id = builder.getId(); 129 this.name = builder.getName(); 130 this.namespace = builder.getNamespace(); 131 this.active = builder.isActive(); 132 this.description = builder.getDescription(); 133 134 this.typeId = builder.getTypeId(); 135 this.agendas = constructAgendas(builder.getAgendas()); 136 this.versionNumber = builder.getVersionNumber(); 137 if (builder.getAttributes() != null){ 138 this.attributes = Collections.unmodifiableMap(new HashMap<String, String>(builder.getAttributes())); 139 } else { 140 this.attributes = null; 141 } 142 } 143 144 private static List<AgendaDefinition> constructAgendas(List<AgendaDefinition.Builder> agendaBuilders) { 145 List<AgendaDefinition> agendas = new ArrayList<AgendaDefinition>(); 146 if (agendaBuilders != null) { 147 for (AgendaDefinition.Builder agendaBuilder : agendaBuilders) { 148 agendas.add(agendaBuilder.build()); 149 } 150 } 151 return agendas; 152 } 153 154 @Override 155 public String getId() { 156 return id; 157 } 158 159 @Override 160 public String getNamespace() { 161 return namespace; 162 } 163 164 @Override 165 public String getName() { 166 return name; 167 } 168 169 @Override 170 public String getTypeId() { 171 return typeId; 172 } 173 174 @Override 175 public String getDescription() { 176 return description; 177 } 178 179 @Override 180 public boolean isActive() { 181 return this.active; 182 } 183 184 @Override 185 public List<AgendaDefinition> getAgendas() { 186 return Collections.unmodifiableList(this.agendas); 187 } 188 189 @Override 190 public Map<String, String> getAttributes() { 191 return this.attributes; 192 } 193 194 @Override 195 public Long getVersionNumber() { 196 return versionNumber; 197 } 198 199 /** 200 * A builder which can be used to construct ContextDefinition instances. Enforces the 201 * constraints of the {@link ContextDefinitionContract}. This class is the only means 202 * by which a {@link ContextDefinition} object can be constructed. 203 * 204 * @author Kuali Rice Team (rice.collab@kuali.org) 205 * 206 */ 207 public static final class Builder implements ContextDefinitionContract, ModelBuilder, Serializable { 208 209 private static final long serialVersionUID = -219369603932108436L; 210 211 private String id; 212 private String namespace; 213 private String name; 214 private String typeId; 215 private String description; 216 private boolean active; 217 private List<AgendaDefinition.Builder> agendas; 218 private Map<String, String> attributes; 219 private Long versionNumber; 220 221 private Builder(String namespace, String name) { 222 setNamespace(namespace); 223 setName(name); 224 setActive(true); 225 setAgendas(new ArrayList<AgendaDefinition.Builder>()); 226 setAttributes(new HashMap<String, String>()); 227 } 228 229 /** 230 * Creates a context definition builder with the given required values 231 * 232 * @param namespace the namespace code of the context definition to create, must not be null or blank 233 * @param name the name of the context definition to create, must not be null or blank 234 * 235 * @return a builder with the required values already initialized 236 * 237 * @throws IllegalArgumentException if the given namespace is null or blank 238 * @throws IllegalArgumentException if the given name is null or blank 239 */ 240 public static Builder create(String namespace, String name) { 241 return new Builder(namespace, name); 242 } 243 244 /** 245 * Creates a populates a builder with the data on the given ContextDefinitionContract. 246 * This is similar in nature to a "copy constructor" for Style. 247 * 248 * @param contract an object implementing the ContextDefinitionContract from which 249 * to copy property values 250 * 251 * @return a builder with the values from the contract already initialized 252 * 253 * @throws IllegalArgumentException if the given contract is null 254 */ 255 public static Builder create(ContextDefinitionContract contract) { 256 if (contract == null) { 257 throw new IllegalArgumentException("contract was null"); 258 } 259 Builder builder = create(contract.getNamespace(), contract.getName()); 260 builder.setId(contract.getId()); 261 builder.setTypeId(contract.getTypeId()); 262 builder.setDescription(contract.getDescription()); 263 builder.setActive(contract.isActive()); 264 builder.setVersionNumber(contract.getVersionNumber()); 265 builder.setAgendas(contract.getAgendas()); 266 if (contract.getAttributes() != null) { 267 builder.setAttributes(new HashMap<String, String>(contract.getAttributes())); 268 } 269 return builder; 270 } 271 272 @Override 273 public ContextDefinition build() { 274 return new ContextDefinition(this); 275 } 276 277 @Override 278 public Long getVersionNumber() { 279 return this.versionNumber; 280 } 281 282 @Override 283 public String getId() { 284 return this.id; 285 } 286 287 @Override 288 public String getNamespace() { 289 return this.namespace; 290 } 291 292 @Override 293 public String getName() { 294 return this.name; 295 } 296 297 @Override 298 public String getTypeId() { 299 return this.typeId; 300 } 301 302 @Override 303 public String getDescription() { 304 return description; 305 } 306 307 @Override 308 public boolean isActive() { 309 return active; 310 } 311 312 @Override 313 public List<AgendaDefinition.Builder> getAgendas() { 314 return agendas; 315 } 316 317 @Override 318 public Map<String, String> getAttributes() { 319 return attributes; 320 } 321 322 /** 323 * Sets the id for the context definition that will be created by this builder. 324 * 325 * @param id the id to set 326 */ 327 public void setId(String id) { 328 if (id != null){ 329 if (StringUtils.isBlank(id)){ 330 throw new IllegalArgumentException("context id is blank"); 331 } 332 } 333 this.id = id; 334 } 335 336 /** 337 * Sets the namespace code for the context definition that will be created 338 * by this builder. The namespace code must not be blank or null. 339 * 340 * @param namespace the namespace to set on this builder, must not be 341 * null or blank 342 * 343 * @throws IllegalArgumentException if the given namespace code 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 name for the context definition that will be created 354 * by this builder. The name must not be blank or null. 355 * 356 * @param name the name to set on this builder, must not be 357 * null or blank 358 * 359 * @throws IllegalArgumentException if the given name is null or blank 360 */ 361 public void setName(String name) { 362 if (StringUtils.isBlank(name)) { 363 throw new IllegalArgumentException("name is blank"); 364 } 365 this.name = name; 366 } 367 368 /** 369 * Sets the typeId for the context definition that will be created by this builder. 370 * 371 * @param typeId the typeId to set 372 */ 373 public void setTypeId(String typeId) { 374 this.typeId = typeId; 375 } 376 377 /** 378 * Sets the description for the context definition that will be created by this builder. 379 * 380 * @param description the descripition to set 381 */ 382 public void setDescription(String description) { 383 this.description = description; 384 } 385 386 /** 387 * Sets the active flag for the context that will be 388 * returned by this builder. 389 * 390 * @param active the active flag to set 391 */ 392 public void setActive(boolean active) { 393 this.active = active; 394 } 395 396 /** 397 * Sets the agendas property of this context definition. 398 * <p>For each of the {@link AgendaDefinitionContract} provided in the parameter list, 399 * construct an AgendaDefinition from the builder of the provided contract, and save the agenda definitions 400 * in a List of {@link AgendaDefinition}</p> 401 * 402 * @param agendaContracts a list of agenda definition contracts 403 */ 404 public void setAgendas(List<? extends AgendaDefinitionContract> agendaContracts) { 405 this.agendas = new ArrayList<AgendaDefinition.Builder>(); 406 if (agendaContracts != null) for (AgendaDefinitionContract agendaContract : agendaContracts) { 407 this.agendas.add(AgendaDefinition.Builder.create(agendaContract)); 408 } 409 } 410 411 /** 412 * Sets the Map of attributes as name / value pairs. 413 * 414 * @param attributes a Map of name value String pairs respresenting the attributes 415 * associated with this context 416 */ 417 public void setAttributes(Map<String, String> attributes){ 418 if (attributes == null){ 419 this.attributes = Collections.emptyMap(); 420 } 421 this.attributes = Collections.unmodifiableMap(attributes); 422 } 423 424 /** 425 * Sets the version number for the style that will be returned by this 426 * builder. 427 * 428 * <p>In general, this value should not be manually set on the builder, 429 * but rather copied from an existing {@link ContextDefinitionContract} when 430 * invoking {@link Builder#create(ContextDefinitionContract)}. 431 * 432 * @param versionNumber the version number to set 433 */ 434 public void setVersionNumber(Long versionNumber) { 435 this.versionNumber = versionNumber; 436 } 437 438 } 439 440 /** 441 * Defines some internal constants used on this class. 442 */ 443 public static class Constants { 444 final static String ROOT_ELEMENT_NAME = "context"; 445 final static String TYPE_NAME = "ContextDefinitionType"; 446 } 447 448 /** 449 * A private class which exposes constants which define the XML element names to use 450 * when this object is marshalled to XML. 451 */ 452 public static class Elements { 453 final static String ID = "id"; 454 final static String NAMESPACE = "namespace"; 455 final static String NAME = "name"; 456 final static String TYPE_ID = "typeId"; 457 final static String DESCRIPTION = "description"; 458 final static String ACTIVE = "active"; 459 final static String AGENDA = "agenda"; 460 final static String AGENDAS = "agendas"; 461 final static String ATTRIBUTES = "attributes"; 462 } 463 464 465 466 }