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