001 /** 002 * Copyright 2005-2013 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.term; 017 018 import java.io.Serializable; 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.Collections; 022 import java.util.List; 023 024 import javax.xml.bind.annotation.XmlAccessType; 025 import javax.xml.bind.annotation.XmlAccessorType; 026 import javax.xml.bind.annotation.XmlAnyElement; 027 import javax.xml.bind.annotation.XmlElement; 028 import javax.xml.bind.annotation.XmlElementWrapper; 029 import javax.xml.bind.annotation.XmlRootElement; 030 import javax.xml.bind.annotation.XmlType; 031 032 import org.apache.commons.lang.StringUtils; 033 import org.kuali.rice.core.api.CoreConstants; 034 import org.kuali.rice.core.api.mo.AbstractDataTransferObject; 035 import org.kuali.rice.core.api.mo.ModelBuilder; 036 import org.kuali.rice.krms.api.KrmsConstants; 037 import org.kuali.rice.krms.api.repository.BuilderUtils; 038 import org.kuali.rice.krms.api.repository.BuilderUtils.Transformer; 039 import org.kuali.rice.krms.api.repository.category.CategoryDefinition; 040 import org.kuali.rice.krms.api.repository.category.CategoryDefinitionContract; 041 042 /** 043 * Immutable DTO for TermSpecifications. Construction must be done via the {@link Builder} inner class. 044 * 045 * @author Kuali Rice Team (rice.collab@kuali.org) 046 * 047 */ 048 @XmlRootElement(name = TermSpecificationDefinition.Constants.ROOT_ELEMENT_NAME) 049 @XmlAccessorType(XmlAccessType.NONE) 050 @XmlType(name = TermSpecificationDefinition.Constants.TYPE_NAME, propOrder = { 051 TermSpecificationDefinition.Elements.ID, 052 TermSpecificationDefinition.Elements.NAME, 053 TermSpecificationDefinition.Elements.NAMESPACE, 054 TermSpecificationDefinition.Elements.TYPE, 055 TermSpecificationDefinition.Elements.DESCRIPTION, 056 TermSpecificationDefinition.Elements.ACTIVE, 057 CoreConstants.CommonElements.VERSION_NUMBER, 058 TermSpecificationDefinition.Elements.CATEGORIES, 059 "contextIds", // has to match the field name -- the element wrapper name does not 060 CoreConstants.CommonElements.FUTURE_ELEMENTS 061 }) 062 public final class TermSpecificationDefinition extends AbstractDataTransferObject implements TermSpecificationDefinitionContract { 063 064 private static final long serialVersionUID = 1L; 065 066 @XmlElement(name = Elements.ID, required=false) 067 private final String id; 068 @XmlElement(name = Elements.NAME, required=true) 069 private final String name; 070 @XmlElement(name = Elements.NAMESPACE, required=true) 071 private final String namespace; 072 @XmlElement(name = Elements.TYPE, required=true) 073 private final String type; 074 @XmlElement(name = Elements.DESCRIPTION, required=false) 075 private final String description; 076 @XmlElement(name = Elements.ACTIVE, required = false) 077 private final boolean active; 078 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false) 079 private final Long versionNumber; 080 081 @XmlElementWrapper(name = Elements.CATEGORIES, required = false) 082 @XmlElement(name = Elements.CATEGORY, required = false) 083 private final List<CategoryDefinition> categories; 084 085 086 @XmlElementWrapper(name = Elements.CONTEXTS, required = false) 087 @XmlElement(name = Elements.CONTEXT_ID, required = false) 088 private final List<String> contextIds; 089 090 @SuppressWarnings("unused") 091 @XmlAnyElement 092 private final Collection<org.w3c.dom.Element> _futureElements = null; 093 094 /** 095 * For JAXB use only, shouldn't ever be invoked directly 096 */ 097 private TermSpecificationDefinition() { 098 id = null; 099 name = null; 100 namespace = null; 101 type = null; 102 description = null; 103 active = true; 104 versionNumber = null; 105 this.categories = null; 106 this.contextIds = null; 107 } 108 109 /** 110 * Private constructor enforces use of Builder 111 * 112 * @param b the builder to use 113 */ 114 private TermSpecificationDefinition(Builder b) { 115 id = b.getId(); 116 name = b.getName(); 117 namespace = b.getNamespace(); 118 type = b.getType(); 119 description = b.getDescription(); 120 active = b.isActive(); 121 versionNumber = b.getVersionNumber(); 122 this.categories = constructCategories(b.getCategories()); 123 this.contextIds = Collections.unmodifiableList(new ArrayList(b.getContextIds())); 124 } 125 126 private static List<CategoryDefinition> constructCategories(List<CategoryDefinition.Builder> categoryBuilders) { 127 List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>(); 128 if (categoryBuilders != null) { 129 for (CategoryDefinition.Builder categoryBuilder : categoryBuilders) { 130 categories.add(categoryBuilder.build()); 131 } 132 } 133 return categories; 134 } 135 136 /** 137 * Builder for the {@link TermSpecificationDefinition} immutable DTO. Instantiate using static factory method(s). 138 * 139 * @author Kuali Rice Team (rice.collab@kuali.org) 140 */ 141 public static class Builder implements TermSpecificationDefinitionContract, ModelBuilder, Serializable { 142 143 private static final long serialVersionUID = 1L; 144 145 private String termSpecificationId; 146 private String name; 147 private String namespace; 148 private String type; 149 private String description; 150 private boolean active; 151 private Long versionNumber; 152 private List<CategoryDefinition.Builder> categories; 153 private List<String> contextIds; 154 155 private static final String NON_NULL_NON_EMPTY_ERROR = " must be non-null and must contain non-whitespace chars"; 156 157 /** 158 * {@link Transformer} to ease converting lists to Builder types 159 */ 160 public static final Transformer<TermSpecificationDefinitionContract, TermSpecificationDefinition.Builder> 161 toBuilder = new BuilderUtils.Transformer<TermSpecificationDefinitionContract, TermSpecificationDefinition.Builder>() { 162 public TermSpecificationDefinition.Builder transform(TermSpecificationDefinitionContract input) { 163 return TermSpecificationDefinition.Builder.create(input); 164 } 165 }; 166 167 private Builder(String termSpecificationId, String name, String namespace, String type) { 168 // weird to use setters in constructor .. oh well. 169 setId(termSpecificationId); 170 setNamespace(namespace); 171 setName(name); 172 setType(type); 173 setActive(true); 174 setCategories(new ArrayList<CategoryDefinition.Builder>()); 175 setContextIds(new ArrayList<String>()); 176 } 177 178 /** 179 * static factory for a {@link Builder} from a complete set of field values for this object. 180 * 181 * 182 * @param termSpecificationId the primary key field. Must be null for service methods that 183 * create {@link org.kuali.rice.krms.api.repository.term.TermSpecificationDefinitionContract}s, and must be non-null & contain non-whitespace 184 * chars otherwise. 185 * @param name the name for the {@link org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition}. Must be non-null;. 186 * @param namespace the namespace for the {@link org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition}. Must be non-null & contain non-whitespace. 187 *@param type the type for the {@link org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition} @return a {@link Builder} object 188 * @throws IllegalArgumentException if invalid parameters are supplied. 189 */ 190 public static Builder create(String termSpecificationId, String name, String namespace, String type) { 191 return new Builder(termSpecificationId, name, namespace, type); 192 } 193 194 /** 195 * static factory for a {@link Builder} from a {@link TermSpecificationDefinitionContract}. 196 * 197 * @param termSpecification may not be null; 198 * @throws IllegalArgumentException if termSpecification is null, or violates the field invariants of the {@link Builder}. 199 */ 200 public static Builder create(TermSpecificationDefinitionContract termSpecification) { 201 if (termSpecification == null) throw new IllegalArgumentException("termSpecification must be non-null"); 202 Builder builder = new Builder(termSpecification.getId(), termSpecification.getName(), termSpecification.getNamespace(), 203 termSpecification.getType()); 204 builder.setDescription(termSpecification.getDescription()); 205 builder.setActive(termSpecification.isActive()); 206 builder.setVersionNumber(termSpecification.getVersionNumber()); 207 for (CategoryDefinitionContract category : termSpecification.getCategories()) { 208 builder.getCategories().add(CategoryDefinition.Builder.create(category)); 209 } 210 if (termSpecification.getContextIds() != null) { 211 builder.getContextIds().addAll(termSpecification.getContextIds()); 212 } 213 214 return builder; 215 } 216 217 public void setDescription(String description) { 218 this.description = description; 219 } 220 221 // Setters 222 223 /** 224 * @param termSpecificationId the key for this {@link TermSpecificationDefinition}. Must be null for 225 * service methods that create {@link TermSpecificationDefinitionContract}s, and otherwise must be non-null & contain 226 * non-whitespace chars. 227 */ 228 public void setId(String termSpecificationId) { 229 if (termSpecificationId != null && StringUtils.isBlank(termSpecificationId)) 230 throw new IllegalArgumentException("termSpecificationId must contain non-whitespace chars"); 231 this.termSpecificationId = termSpecificationId; 232 } 233 234 /** 235 * @param namespace the namespace to set. Must be non-null and contain non-whitespace chars; 236 */ 237 public void setNamespace(String namespace) { 238 if (StringUtils.isBlank(namespace)) { 239 throw new IllegalArgumentException("namespace" + NON_NULL_NON_EMPTY_ERROR); 240 } 241 this.namespace = namespace; 242 } 243 244 /** 245 * @param name the name to set. Must be non-null and contain non-whitespace chars; 246 */ 247 public void setName(String name) { 248 if (StringUtils.isBlank(name)) { 249 throw new IllegalArgumentException("name" + NON_NULL_NON_EMPTY_ERROR); 250 } 251 this.name = name; 252 } 253 /** 254 * @param type the type to set. Must be non-null and contain non-whitespace chars; 255 */ 256 public void setType(String type) { 257 if (StringUtils.isBlank(type)) { 258 throw new IllegalArgumentException("type" + NON_NULL_NON_EMPTY_ERROR); 259 } 260 this.type = type; 261 } 262 263 /** 264 * @param versionNumber the versionNumber to set. May be null. 265 */ 266 public void setVersionNumber(Long versionNumber){ 267 this.versionNumber = versionNumber; 268 } 269 270 public void setActive(boolean active) { 271 this.active = active; 272 } 273 274 /** 275 * @param categories the categories to set. May not be null but can be an empty set. 276 */ 277 public void setCategories(List<CategoryDefinition.Builder> categories) { 278 if (categories == null) { 279 throw new IllegalArgumentException("categories was null"); 280 } 281 this.categories = categories; 282 } 283 284 /** 285 * @param contextIds the contextIds to set. May not be null but may be empty. 286 */ 287 public void setContextIds(List<String> contextIds) { 288 if (contextIds == null) { 289 throw new IllegalArgumentException("contextIds was null"); 290 } 291 this.contextIds = contextIds; 292 } 293 294 // Getters 295 296 /** 297 * @return the termSpecificationId 298 */ 299 @Override 300 public String getId() { 301 return this.termSpecificationId; 302 } 303 304 /** 305 * @return the namespace 306 */ 307 @Override 308 public String getNamespace() { 309 return this.namespace; 310 } 311 312 /** 313 * @return the name 314 */ 315 @Override 316 public String getName() { 317 return this.name; 318 } 319 320 /** 321 * @return the type 322 */ 323 @Override 324 public String getType() { 325 return this.type; 326 } 327 328 @Override 329 public String getDescription() { 330 return this.description; 331 } 332 333 /** 334 * @return the version number 335 */ 336 @Override 337 public Long getVersionNumber() { 338 return this.versionNumber; 339 } 340 341 @Override 342 public boolean isActive() { 343 return active; 344 } 345 346 /** 347 * @return the categories 348 */ 349 @Override 350 public List<CategoryDefinition.Builder> getCategories() { 351 return this.categories; 352 } 353 354 @Override 355 public List<String> getContextIds() { 356 return contextIds; 357 } 358 359 /** 360 * Constructs a {@link TermSpecificationDefinition} 361 * @see org.kuali.rice.core.api.mo.ModelBuilder#build() 362 */ 363 @Override 364 public TermSpecificationDefinition build() { 365 return new TermSpecificationDefinition(this); 366 } 367 } 368 369 /** 370 * This value will be non-null for persisted 371 * @see org.kuali.rice.krms.api.repository.term.TermSpecificationDefinitionContract#getId() 372 */ 373 @Override 374 public String getId() { 375 return id; 376 } 377 378 /** 379 * @see org.kuali.rice.krms.api.repository.term.TermSpecificationDefinitionContract#getName() 380 */ 381 @Override 382 public String getName() { 383 return name; 384 } 385 386 @Override 387 public String getNamespace() { 388 return namespace; 389 } 390 391 /** 392 * @see org.kuali.rice.krms.api.repository.term.TermSpecificationDefinitionContract#getType() 393 */ 394 @Override 395 public String getType() { 396 return type; 397 } 398 399 @Override 400 public String getDescription() { 401 return description; 402 } 403 404 @Override 405 public boolean isActive() { 406 return active; 407 } 408 409 /** 410 * @see org.kuali.rice.core.api.mo.common.Versioned#getVersionNumber() 411 */ 412 @Override 413 public Long getVersionNumber() { 414 return versionNumber; 415 } 416 417 /** 418 * @see TermSpecificationDefinitionContract#getCategories() 419 */ 420 @Override 421 public List<CategoryDefinition> getCategories() { 422 return Collections.unmodifiableList(categories); 423 } 424 425 @Override 426 public List<String> getContextIds() { 427 return contextIds; 428 } 429 430 /** 431 * Defines some internal constants used on this class. 432 */ 433 static class Constants { 434 final static String ROOT_ELEMENT_NAME = "termSpecification"; 435 final static String TYPE_NAME = "TermSpecificationType"; 436 } 437 438 static class Elements { 439 public static final String ID = "id"; 440 public static final String NAME = "name"; 441 public final static String NAMESPACE = "namespace"; 442 public static final String TYPE = "type"; 443 public static final String DESCRIPTION = "description"; 444 public static final String ACTIVE = "active"; 445 public final static String CATEGORIES = "categories"; 446 public final static String CATEGORY = "category"; 447 public final static String CONTEXTS = "contexts"; 448 public final static String CONTEXT_ID = "contextId"; 449 } 450 451 public static class Cache { 452 public static final String NAME = KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0 + "/" + TermSpecificationDefinition.Constants.TYPE_NAME; 453 } 454 }