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