001    /**
002     * Copyright 2005-2012 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.kim.api.type;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.CoreConstants;
020    import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
021    import org.kuali.rice.core.api.mo.ModelBuilder;
022    import org.kuali.rice.kim.api.KimConstants;
023    import org.w3c.dom.Element;
024    
025    import javax.xml.bind.annotation.XmlAccessType;
026    import javax.xml.bind.annotation.XmlAccessorType;
027    import javax.xml.bind.annotation.XmlAnyElement;
028    import javax.xml.bind.annotation.XmlElement;
029    import javax.xml.bind.annotation.XmlElementWrapper;
030    import javax.xml.bind.annotation.XmlRootElement;
031    import javax.xml.bind.annotation.XmlType;
032    import java.io.Serializable;
033    import java.util.ArrayList;
034    import java.util.Collection;
035    import java.util.Collections;
036    import 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    })
058    public 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    }