001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krms.api.repository.context;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreConstants;
020import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
021import org.kuali.rice.core.api.mo.ModelBuilder;
022import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter;
023import org.kuali.rice.krms.api.KrmsConstants;
024import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
025import org.kuali.rice.krms.api.repository.agenda.AgendaDefinitionContract;
026
027import javax.xml.bind.annotation.XmlAccessType;
028import javax.xml.bind.annotation.XmlAccessorType;
029import javax.xml.bind.annotation.XmlAnyElement;
030import javax.xml.bind.annotation.XmlElement;
031import javax.xml.bind.annotation.XmlElementWrapper;
032import javax.xml.bind.annotation.XmlRootElement;
033import javax.xml.bind.annotation.XmlType;
034import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
035import java.io.Serializable;
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.HashMap;
040import java.util.List;
041import 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})
070public 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}