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.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 }