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.term;
017
018import java.io.Serializable;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.List;
023
024import javax.xml.bind.annotation.XmlAccessType;
025import javax.xml.bind.annotation.XmlAccessorType;
026import javax.xml.bind.annotation.XmlAnyElement;
027import javax.xml.bind.annotation.XmlElement;
028import javax.xml.bind.annotation.XmlElementWrapper;
029import javax.xml.bind.annotation.XmlRootElement;
030import javax.xml.bind.annotation.XmlType;
031
032import org.apache.commons.lang.StringUtils;
033import org.kuali.rice.core.api.CoreConstants;
034import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
035import org.kuali.rice.core.api.mo.ModelBuilder;
036import org.kuali.rice.krms.api.KrmsConstants;
037import org.kuali.rice.krms.api.repository.BuilderUtils;
038import org.kuali.rice.krms.api.repository.BuilderUtils.Transformer;
039import org.kuali.rice.krms.api.repository.category.CategoryDefinition;
040import 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})
062public 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}