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 }