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.kim.api.type;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreConstants;
020import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
021import org.kuali.rice.core.api.mo.ModelBuilder;
022import org.kuali.rice.kim.api.KimConstants;
023import org.w3c.dom.Element;
024
025import javax.xml.bind.annotation.XmlAccessType;
026import javax.xml.bind.annotation.XmlAccessorType;
027import javax.xml.bind.annotation.XmlAnyElement;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlElementWrapper;
030import javax.xml.bind.annotation.XmlRootElement;
031import javax.xml.bind.annotation.XmlType;
032import java.io.Serializable;
033import java.util.ArrayList;
034import java.util.Collection;
035import java.util.Collections;
036import java.util.List;
037
038/**
039 * An immutable representation of a {@link KimTypeContract}.
040 *
041 * <p>To construct an instance of a KimType, use the {@link KimType.Builder} class.<p/>
042 *
043 * @see KimTypeContract
044 */
045@XmlRootElement(name = KimType.Constants.ROOT_ELEMENT_NAME)
046@XmlAccessorType(XmlAccessType.NONE)
047@XmlType(name = KimType.Constants.TYPE_NAME, propOrder = {
048        KimType.Elements.ID,
049        KimType.Elements.SERVICE_NAME,
050        KimType.Elements.NAMESPACE_CODE,
051        KimType.Elements.NAME,
052        KimType.Elements.ATTRIBUTE_DEFNS,
053        KimType.Elements.ACTIVE,
054        CoreConstants.CommonElements.VERSION_NUMBER,
055        CoreConstants.CommonElements.OBJECT_ID,
056        CoreConstants.CommonElements.FUTURE_ELEMENTS
057})
058public final class KimType extends AbstractDataTransferObject implements KimTypeContract {
059    private static final long serialVersionUID = 1L;
060
061    @XmlElement(name = KimType.Elements.ID, required = false)
062    private final String id;
063
064    @XmlElement(name = KimType.Elements.SERVICE_NAME, required = false)
065    private final String serviceName;
066
067    @XmlElement(name = KimType.Elements.NAMESPACE_CODE, required = false)
068    private final String namespaceCode;
069
070    @XmlElement(name = KimType.Elements.NAME, required = false)
071    private final String name;
072
073    @XmlElementWrapper(name = Elements.ATTRIBUTE_DEFNS, required = false)
074    @XmlElement(name = KimType.Elements.ATTRIBUTE_DEFN, required = false)
075    private final List<KimTypeAttribute> attributeDefinitions;
076
077    @XmlElement(name = KimType.Elements.ACTIVE, required = false)
078    private final boolean active;
079
080    @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
081    private final Long versionNumber;
082
083    @XmlElement(name = CoreConstants.CommonElements.OBJECT_ID, required = false)
084    private final String objectId;
085
086    @SuppressWarnings("unused")
087    @XmlAnyElement
088    private final Collection<Element> _futureElements = null;
089
090    /**
091     * This constructor should never be called except during JAXB unmarshalling.
092     */
093    private KimType() {
094        this.id = null;
095        this.serviceName = null;
096        this.namespaceCode = null;
097        this.name = null;
098        this.attributeDefinitions = Collections.<KimTypeAttribute>emptyList();
099        this.active = false;
100        this.versionNumber = Long.valueOf(1L);
101        this.objectId = null;
102    }
103
104    private KimType(Builder builder) {
105        this.id = builder.getId();
106        this.serviceName = builder.getServiceName();
107        this.namespaceCode = builder.getNamespaceCode();
108        this.name = builder.getName();
109        final List<KimTypeAttribute> temp = new ArrayList<KimTypeAttribute>();
110        for (KimTypeAttribute.Builder attr : builder.getAttributeDefinitions()) {
111            //associate each attribute with this kimType's id
112            attr.setKimTypeId(this.id);
113            temp.add(attr.build());
114        }
115        this.attributeDefinitions = Collections.unmodifiableList(temp);
116
117        this.active = builder.isActive();
118        this.versionNumber = builder.getVersionNumber();
119        this.objectId = builder.getObjectId();
120    }
121
122    /**
123     * Gets the KimTypeAttribute matching the id of it's KimAttribute.  If no attribute definition exists with that
124     * id then null is returned.
125     *
126     * <p>
127     * If multiple exist with the same id then the first match is returned.  Since id
128     * is supposed to be unique this should not be a problem in practice.
129     * </p>
130     *
131     * @param id the KimTypeAttribute.KimAttribute's id
132     * @return the KimTypeAttribute or null
133     * @throws IllegalArgumentException if the id is blank
134     */
135    public KimTypeAttribute getAttributeDefinitionById(String id) {
136        if (StringUtils.isBlank(id)) {
137            throw new IllegalArgumentException("id is blank");
138        }
139
140        if (this.attributeDefinitions != null) {
141            for (KimTypeAttribute att : this.attributeDefinitions) {
142                if (att != null && att.getKimAttribute() != null
143                        && id.equals(att.getKimAttribute().getId())) {
144                    return att;
145                }
146            }
147        }
148                return null;
149        }
150
151    /**
152     * Gets the KimTypeAttribute matching the name of it's KimAttribute.  If no attribute definition exists with that
153     * name then null is returned.
154     *
155     * <p>
156     * If multiple exist with the same name then the first match is returned.  Since name
157     * is supposed to be unique this should not be a problem in practice.
158     * </p>
159     *
160     * @param name the KimTypeAttribute's name
161     * @return the KimTypeAttribute or null
162     * @throws IllegalArgumentException if the name is blank
163     */
164        public KimTypeAttribute getAttributeDefinitionByName(String name) {
165        if (StringUtils.isBlank(name)) {
166            throw new IllegalArgumentException("name is blank");
167        }
168
169        if (this.attributeDefinitions != null) {
170            for (KimTypeAttribute att : this.attributeDefinitions) {
171                if (att != null && att.getKimAttribute() != null
172                        && name.equals(att.getKimAttribute().getAttributeName())) {
173                    return att;
174                }
175            }
176        }
177                return null;
178        }
179
180    @Override
181    public String getId() {
182        return id;
183    }
184
185    @Override
186    public String getServiceName() {
187        return serviceName;
188    }
189
190    @Override
191    public String getNamespaceCode() {
192        return namespaceCode;
193    }
194
195    @Override
196    public String getName() {
197        return name;
198    }
199
200    @Override
201    public List<KimTypeAttribute> getAttributeDefinitions() {
202        return attributeDefinitions;
203    }
204
205    @Override
206    public boolean isActive() {
207        return active;
208    }
209
210    @Override
211    public Long getVersionNumber() {
212        return versionNumber;
213    }
214
215    @Override
216    public String getObjectId() {
217        return objectId;
218    }
219
220    /**
221     * This builder constructs an KimType enforcing the constraints of the {@link KimTypeContract}.
222     */
223    public static final class Builder implements KimTypeContract, ModelBuilder, Serializable {
224        private String id;
225        private String serviceName;
226        private String namespaceCode;
227        private String name;
228        private List<KimTypeAttribute.Builder> attributeDefinitions = new ArrayList<KimTypeAttribute.Builder>();
229        private boolean active;
230        private Long versionNumber = 1L;
231        private String objectId;
232
233        private Builder() {
234        }
235
236        /**
237         * creates a KimType with the required fields.
238         */
239        public static Builder create() {
240            return new Builder();
241        }
242
243        /**
244         * creates a KimType from an existing {@link KimTypeContract}.
245         */
246        public static Builder create(KimTypeContract contract) {
247                if (contract == null) {
248                        throw new IllegalArgumentException("contract was null");
249                }
250            Builder builder = new Builder();
251            builder.setId(contract.getId());
252            builder.setServiceName(contract.getServiceName());
253            builder.setNamespaceCode(contract.getNamespaceCode());
254            builder.setName(contract.getName());
255
256            if (contract.getAttributeDefinitions() != null) {
257                final List<KimTypeAttribute.Builder> temp = new ArrayList<KimTypeAttribute.Builder>();
258                for (KimTypeAttributeContract attr : contract.getAttributeDefinitions()) {
259                    temp.add(KimTypeAttribute.Builder.create(attr));
260                }
261
262                builder.setAttributeDefinitions(Collections.unmodifiableList(temp));
263            }
264
265            builder.setActive(contract.isActive());
266            builder.setVersionNumber(contract.getVersionNumber());
267            builder.setObjectId(contract.getObjectId());
268            return builder;
269        }
270
271        @Override
272        public String getId() {
273            return id;
274        }
275
276        public void setId(final String id) {
277            this.id = id;
278        }
279
280        @Override
281        public String getServiceName() {
282            return serviceName;
283        }
284
285        public void setServiceName(final String serviceName) {
286            this.serviceName = serviceName;
287        }
288
289        @Override
290        public String getNamespaceCode() {
291            return namespaceCode;
292        }
293
294        public void setNamespaceCode(final String namespaceCode) {
295            this.namespaceCode = namespaceCode;
296        }
297
298        @Override
299        public String getName() {
300            return name;
301        }
302
303        public void setName(final String name) {
304            this.name = name;
305        }
306
307        @Override
308        public List<KimTypeAttribute.Builder> getAttributeDefinitions() {
309            return attributeDefinitions;
310        }
311
312        public void setAttributeDefinitions(final List<KimTypeAttribute.Builder> attributeDefinitions) {
313            if (attributeDefinitions == null) {
314                throw new IllegalArgumentException("attributeDefinitions is null");
315            }
316
317            this.attributeDefinitions = attributeDefinitions;
318        }
319
320        @Override
321        public boolean isActive() {
322            return active;
323        }
324
325        public void setActive(final boolean active) {
326            this.active = active;
327        }
328
329        @Override
330        public Long getVersionNumber() {
331            return versionNumber;
332        }
333
334        public void setVersionNumber(final Long versionNumber) {
335            if (versionNumber != null && versionNumber <= 0) {
336                throw new IllegalArgumentException("versionNumber is invalid");
337            }
338
339            this.versionNumber = versionNumber;
340        }
341
342        @Override
343        public String getObjectId() {
344            return objectId;
345        }
346
347        public void setObjectId(final String objectId) {
348            this.objectId = objectId;
349        }
350
351        @Override
352        public KimType build() {
353            return new KimType(this);
354        }
355    }
356
357    /**
358     * Defines some internal constants used on this class.
359     */
360    static class Constants {
361        static final String ROOT_ELEMENT_NAME = "kimType";
362        static final String TYPE_NAME = "KimTypeType";
363    }
364
365    /**
366     * A private class which exposes constants which define the XML element names to use
367     * when this object is marshalled to XML.
368     */
369    static class Elements {
370        static final String ID = "id";
371        static final String SERVICE_NAME = "serviceName";
372        static final String NAMESPACE_CODE = "namespaceCode";
373        static final String NAME = "name";
374        static final String ATTRIBUTE_DEFNS = "attributeDefinitions";
375        static final String ATTRIBUTE_DEFN = "attributeDefinition";
376        static final String ACTIVE = "active";
377    }
378
379    public static class Cache {
380        public static final String NAME = KimConstants.Namespaces.KIM_NAMESPACE_2_0 + "/" + KimType.Constants.TYPE_NAME;
381    }
382}