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