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    }