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    }