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    }