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