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