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 }