001    /*
002     *  jDTAUS - DTAUS fileformat.
003     *  Copyright (c) 2005 Christian Schulte <cs@schulte.it>
004     *
005     *  This library is free software; you can redistribute it and/or
006     *  modify it under the terms of the GNU Lesser General Public
007     *  License as published by the Free Software Foundation; either
008     *  version 2.1 of the License, or any later version.
009     *
010     *  This library is distributed in the hope that it will be useful,
011     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
012     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     *  Lesser General Public License for more details.
014     *
015     *  You should have received a copy of the GNU Lesser General Public
016     *  License along with this library; if not, write to the Free Software
017     *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
018     *
019     */
020    package org.jdtaus.core.container;
021    
022    import java.io.Serializable;
023    import java.util.Arrays;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.Map;
027    
028    /**
029     * Collection of specifications.
030     *
031     * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
032     * @version $Id: Specifications.java 2230 2007-03-26 01:37:48Z schulte2005 $
033     */
034    public class Specifications implements Cloneable, Serializable
035    {
036    
037        //--Specifications----------------------------------------------------------
038    
039        /**
040         * The specifications of the collection.
041         * @serial
042         */
043        private Specification[] specifications;
044    
045        /**
046         * Maps specification identifiers to specifications.
047         * @serial
048         */
049        private final Map identifiers = new HashMap();
050    
051        /**
052         * Hash code.
053         * @serial
054         */
055        private int hashCode;
056    
057        /**
058         * Gets the specifications of the collection.
059         *
060         * @return the specifications of the collection.
061         */
062        public Specification[] getSpecifications()
063        {
064            if(this.specifications == null)
065            {
066                this.specifications = new Specification[0];
067                this.hashCode = 0;
068            }
069    
070            return this.specifications;
071        }
072    
073        /**
074         * Setter for property {@code specifications}.
075         *
076         * @param value the new specifications for the instance.
077         *
078         * @throws DuplicateSpecificationException if {@code value} contains
079         * duplicate specifications.
080         */
081        public void setSpecifications(final Specification[] value)
082        {
083            this.identifiers.clear();
084            this.hashCode = 0;
085            this.specifications = value;
086    
087            if(value != null)
088            {
089                for(int i = value.length - 1; i >= 0; i--)
090                {
091                    this.hashCode += value[i].hashCode();
092                    if(this.identifiers.put(value[i].getIdentifier(),
093                        value[i]) != null)
094                    {
095                        throw new DuplicateSpecificationException(
096                            value[i].getIdentifier());
097    
098                    }
099                }
100            }
101        }
102    
103        /**
104         * Gets a specification for an identifier.
105         *
106         * @param identifier the identifier of the specification to return.
107         *
108         * @return a reference to the specification identified by
109         * {@code identifier}.
110         *
111         * @throws NullPointerException if {@code identifier} is {@code null}.
112         * @throws MissingSpecificationException if no specification matching
113         * {@code identifier} exists in the collection.
114         */
115        public Specification getSpecification(final String identifier)
116        {
117    
118            if(identifier == null)
119            {
120                throw new NullPointerException("identifier");
121            }
122    
123            final Specification ret =
124                (Specification) this.identifiers.get(identifier);
125    
126            if(ret == null)
127            {
128                throw new MissingSpecificationException(identifier);
129            }
130    
131            return ret;
132        }
133    
134        /**
135         * Gets a specification for an index.
136         *
137         * @param index the index of the specification to return.
138         *
139         * @return a reference to the specification at {@code index}.
140         *
141         * @throws IndexOutOfBoundsException if {@code index} is negativ,
142         * greater than or equal to {@code size()}.
143         */
144        public final Specification getSpecification(final int index)
145        {
146            if(index < 0 || index >= this.size())
147            {
148                throw new ArrayIndexOutOfBoundsException(index);
149            }
150    
151            return this.getSpecifications()[index];
152        }
153    
154        /**
155         * Gets the number of specifications held by the instance.
156         *
157         * @return the number of specifications held by the instance.
158         */
159        public final int size()
160        {
161            return this.getSpecifications().length;
162        }
163    
164        /**
165         * Creates a string representing the properties of the instance.
166         *
167         * @return a string representing the properties of the instance.
168         */
169        private String internalString()
170        {
171            final StringBuffer buf = new StringBuffer(200);
172            final Specification[] specifications = this.getSpecifications();
173            for(int i = specifications.length - 1; i >= 0; i--)
174            {
175                buf.append("\n\tspecification[").append(i).append("]=").
176                    append(specifications[i]);
177    
178            }
179    
180            return buf.toString();
181        }
182    
183        //----------------------------------------------------------Specifications--
184        //--Object------------------------------------------------------------------
185    
186        /**
187         * Indicates whether some other object is equal to this one by comparing
188         * the values of all properties.
189         *
190         * @param o the reference object with which to compare.
191         *
192         * @return {@code true} if this object is the same as {@code o};
193         * {@code false} otherwise.
194         */
195        public boolean equals(final Object o)
196        {
197            boolean equal = this == o;
198    
199            if(!equal && o instanceof Specifications)
200            {
201                final Specifications that = (Specifications) o;
202                final Collection these = Arrays.asList(this.getSpecifications());
203                final Collection those = Arrays.asList(that.getSpecifications());
204    
205                equal = these.size() == that.size() && these.containsAll(those);
206            }
207    
208            return equal;
209        }
210    
211        /**
212         * Returns a hash code value for this object.
213         *
214         * @return a hash code value for this object.
215         */
216        public int hashCode()
217        {
218            return this.hashCode;
219        }
220    
221        /**
222         * Returns a string representation of the object.
223         *
224         * @return a string representation of the object.
225         */
226        public String toString()
227        {
228            return super.toString() + this.internalString();
229        }
230    
231        /**
232         * Creates and returns a deep copy of this object.
233         *
234         * @return a clone of this instance.
235         */
236        public Object clone()
237        {
238            try
239            {
240                final Specifications ret = (Specifications) super.clone();
241                final Specification[] specs = this.getSpecifications();
242                ret.specifications = new Specification[specs.length];
243    
244                for(int i = specs.length - 1; i >= 0; i--)
245                {
246                    ret.specifications[i] = (Specification) specs[i].clone();
247                }
248    
249                return ret;
250            }
251            catch(CloneNotSupportedException e)
252            {
253                throw new AssertionError(e);
254            }
255        }
256    
257        //------------------------------------------------------------------Object--
258    }