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.agenda;
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;
024
025import javax.xml.bind.annotation.XmlAccessType;
026import javax.xml.bind.annotation.XmlAccessorType;
027import javax.xml.bind.annotation.XmlAnyElement;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlRootElement;
030import javax.xml.bind.annotation.XmlType;
031import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
032import java.io.Serializable;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.HashMap;
036import java.util.Map;
037
038/**
039 * Concrete model object implementation of KRMS Repository Agenda 
040 * immutable. 
041 * Instances of Agenda can be (un)marshalled to and from XML.
042 *
043 * @see AgendaDefinitionContract
044 */
045@XmlRootElement(name = AgendaDefinition.Constants.ROOT_ELEMENT_NAME)
046@XmlAccessorType(XmlAccessType.NONE)
047@XmlType(name = AgendaDefinition.Constants.TYPE_NAME, propOrder = {
048                AgendaDefinition.Elements.AGENDA_ID,
049                AgendaDefinition.Elements.NAME,
050                AgendaDefinition.Elements.TYPE_ID,
051                AgendaDefinition.Elements.CONTEXT_ID,
052        AgendaDefinition.Elements.ACTIVE,
053                AgendaDefinition.Elements.FIRST_ITEM_ID,
054                AgendaDefinition.Elements.ATTRIBUTES,
055        CoreConstants.CommonElements.VERSION_NUMBER,
056                CoreConstants.CommonElements.FUTURE_ELEMENTS
057})
058public final class AgendaDefinition extends AbstractDataTransferObject implements AgendaDefinitionContract {
059        private static final long serialVersionUID = 2783959459503209577L;
060
061        @XmlElement(name = Elements.AGENDA_ID, required = false)
062        private final String id;
063        
064        @XmlElement(name = Elements.NAME, required = true)
065        private final String name;
066        
067        @XmlElement(name = Elements.TYPE_ID, required = false)
068        private final String typeId;
069        
070        @XmlElement(name = Elements.CONTEXT_ID, required = true)
071        private final String contextId;
072
073    @XmlElement(name = Elements.ACTIVE, required = false)
074    private final boolean active;
075        
076        @XmlElement(name = Elements.FIRST_ITEM_ID, required = false)
077        private final String firstItemId;
078        
079        @XmlElement(name = Elements.ATTRIBUTES, required = false)
080        @XmlJavaTypeAdapter(value = MapStringStringAdapter.class)
081        private final Map<String, String> attributes;
082        
083    @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
084    private final Long versionNumber;
085
086    @SuppressWarnings("unused")
087    @XmlAnyElement
088    private final Collection<org.w3c.dom.Element> _futureElements = null;
089        
090        /** 
091     * This constructor should never be called.  
092     * It is only present for use during JAXB unmarshalling. 
093     */
094    private AgendaDefinition() {
095        this.id = null;
096        this.name = null;
097        this.typeId = null;
098        this.contextId = null;
099        this.active = true;
100        this.firstItemId = null;
101        this.attributes = null;
102        this.versionNumber = null;
103    }
104    
105    /**
106         * Constructs a KRMS Repository Agenda object from the given builder.  
107         * This constructor is private and should only ever be invoked from the builder.
108         * 
109         * @param builder the Builder from which to construct the Agenda
110         */
111    private AgendaDefinition(Builder builder) {
112        this.id = builder.getId();
113        this.name = builder.getName();
114        this.typeId = builder.getTypeId();
115        this.contextId = builder.getContextId();
116        this.active = builder.isActive();
117        this.firstItemId = builder.getFirstItemId();
118        if (builder.getAttributes() != null){
119                this.attributes = Collections.unmodifiableMap(new HashMap<String, String>(builder.getAttributes()));
120        } else {
121                this.attributes = null;
122        }
123        this.versionNumber = builder.getVersionNumber();
124    }
125    
126        @Override
127        public String getId() {
128                return this.id;
129        }
130
131        @Override
132        public String getName() {
133                return this.name;
134        }
135
136        @Override
137        public String getTypeId() {
138                return this.typeId;
139        }
140
141        @Override
142        public String getContextId(){
143                return this.contextId;
144        }
145
146    @Override
147    public boolean isActive() {
148        return this.active;
149    }
150
151        @Override
152        public String getFirstItemId(){
153                return this.firstItemId;
154        }
155
156    /**
157     * Returns the internal list of custom/remote attributes associated with the
158     * agenda.
159     *
160     * @return the internal list of custom/remote attribute of the agenda.
161     */
162        public Map<String, String> getAttributes() {
163                return this.attributes; 
164        }
165
166    @Override
167    public Long getVersionNumber() {
168        return versionNumber;
169    }
170    
171        /**
172     * This builder is used to construct instances of KRMS Repository Agenda.  It enforces the constraints of the {@link AgendaDefinitionContract}.
173     */
174    public static class Builder implements AgendaDefinitionContract, ModelBuilder, Serializable {
175                
176        private static final long serialVersionUID = -8862851720709537839L;
177        
178                private String id;
179        private String name;
180        private String typeId;
181        private String contextId;
182        private boolean active;
183        private String firstItemId;
184        private Map<String, String> attributes;
185        private Long versionNumber;
186
187                /**
188                 * Private constructor for creating a builder with all of it's required attributes.
189                 */
190        private Builder(String id, String name, String typeId, String contextId) {
191                setId(id);
192            setName(name);
193            setTypeId(typeId);
194            setContextId(contextId);
195            setActive(true);
196            setAttributes(new HashMap<String, String>());
197        }
198
199         /**
200          * Create a builder with the given parameters.
201          *
202          * @param id
203          * @param name
204          * @param typeId
205          * @param contextId
206          * @return Builder
207          */
208        public static Builder create(String id, String name, String typeId, String contextId){
209                return new Builder(id, name, typeId, contextId);
210        }
211
212        /**
213         * Creates a builder by populating it with data from the given {@link AgendaDefinitionContract}.
214         * 
215         * @param contract the contract from which to populate this builder
216         * @return an instance of the builder populated with data from the contract
217         * @throws IllegalArgumentException if the contract is null
218         */
219        public static Builder create(AgendaDefinitionContract contract) {
220                if (contract == null) {
221                throw new IllegalArgumentException("contract is null");
222            }
223            Builder builder =  new Builder(contract.getId(), contract.getName(), contract.getTypeId(), contract.getContextId());
224            builder.setActive(contract.isActive());
225            builder.setFirstItemId( contract.getFirstItemId() );
226            if (contract.getAttributes() != null) {
227                builder.setAttributes(new HashMap<String, String>(contract.getAttributes()));
228            }
229            builder.setVersionNumber(contract.getVersionNumber());
230            return builder;
231        }
232
233                /**
234                 * Sets the value of the id on this builder to the given value.
235                 * 
236                 * @param agendaId the agenda id value to set, may be null, must not be blank
237         * <p>The agenda id is generated by the system.  For new agendas (not yet persisted) this field is null.
238         *    For existing agendas this field is the generated id.</p>
239                 * @throws IllegalArgumentException if the id is blank
240                 */
241        public void setId(String agendaId) {
242            if (agendaId != null && StringUtils.isBlank(agendaId)) {
243                throw new IllegalArgumentException("agenda ID must be null or non-blank");
244            }
245                        this.id = agendaId;
246                }
247
248        /**
249         * Set the value of the name on this builder to the given value.
250         *
251         * @param name the name of the agenda to set, must not be null or blank
252         * @throws IllegalArgumentException if the name is null or blank
253         */
254        public void setName(String name) {
255            if (StringUtils.isBlank(name)) {
256                throw new IllegalArgumentException("name is blank");
257            }
258                        this.name = name;
259                }
260
261         /**
262          * Set the value of the type id on this builder to the given value.
263          * @param typeId the type id of the agenda to set
264          */
265                public void setTypeId(String typeId) {
266                        this.typeId = typeId;
267                }
268
269        /**
270         * Set the value of the context id on this builder to the given value.
271         *
272         * @param contextId the context id of the agenda to set, must not be null or blank
273         * @throws IllegalArgumentException if the name is null or blank
274         */
275                public void setContextId(String contextId) {
276                        if (StringUtils.isBlank(contextId)) {
277                throw new IllegalArgumentException("context id is blank");
278                }
279                        this.contextId = contextId;
280                }
281
282         /**
283          * Set the value of the active indicator on this builder to the given value.
284          *
285          * @param active the active indicator of the agenda to set
286          */
287        public void setActive(boolean active) {
288            this.active = active;
289        }
290
291         /**
292          * Set the value of the first agenda item id on this builder to the given value.
293          *
294          * @param firstItemId the first agenda item of the agenda tree to set
295          */
296                public void setFirstItemId(String firstItemId) {
297                        this.firstItemId = firstItemId;
298                }
299
300         /**
301          * Set the value of the remote/custom attributes on this builder to the given value.
302          *
303          * @param attributes the remote/custom attributes of the agenda to set
304          */
305                public void setAttributes(Map<String, String> attributes){
306                        if (attributes == null){
307                                this.attributes = Collections.emptyMap();
308                        }
309                        this.attributes = Collections.unmodifiableMap(attributes);
310                }
311                
312                /**
313         * Sets the version number on this builder to the given value.
314         *
315         * @param versionNumber the version number to set
316         */
317        public void setVersionNumber(Long versionNumber){
318            this.versionNumber = versionNumber;
319        }
320        
321                @Override
322                public String getId() {
323                        return id;
324                }
325
326                @Override
327                public String getName() {
328                        return name;
329                }
330
331                @Override
332                public String getTypeId() {
333                        return typeId;
334                }
335
336                @Override
337                public String getContextId() {
338                        return contextId;
339                }
340
341        @Override
342        public boolean isActive() {
343            return active;
344        }
345
346                @Override
347                public String getFirstItemId() {
348                        return firstItemId;
349                }
350
351                @Override
352                public Map<String, String> getAttributes() {
353                        return attributes;
354                }
355
356        @Override
357        public Long getVersionNumber() {
358            return versionNumber;
359        }
360
361                /**
362                 * Builds an instance of a Agenda based on the current state of the builder.
363                 * 
364                 * @return the fully-constructed Agenda
365                 */
366        @Override
367        public AgendaDefinition build() {
368            return new AgendaDefinition(this);
369        }
370                
371    }
372        
373        /**
374         * Defines some constants used on this class.
375         */
376        public static class Constants {
377                final static String ROOT_ELEMENT_NAME = "agenda";
378                final static String TYPE_NAME = "AgendaType";
379                final static String[] HASH_CODE_EQUALS_EXCLUDE = { "_futureElements" };
380        public final static String EVENT = "Event";   // key for event attribute
381        }
382        
383        /**
384         * A private class which exposes constants which define the XML element names to use
385         * when this object is marshalled to XML.
386         */
387        public static class Elements {
388                final static String AGENDA_ID = "id";
389                final static String NAME = "name";
390                final static String TYPE_ID = "typeId";
391                final static String CONTEXT_ID = "contextId";
392        final static String ACTIVE = "active";
393                final static String FIRST_ITEM_ID = "firstItemId";
394                final static String ATTRIBUTES = "attributes";
395                final static String ATTRIBUTE = "attribute";
396        }
397
398    public static class Cache {
399        public static final String NAME = KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0 + "/" + AgendaDefinition.Constants.TYPE_NAME;
400    }
401}