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 implementations.
031 *
032 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
033 * @version $JDTAUS: Implementations.java 8743 2012-10-07 03:06:20Z schulte $
034 */
035public class Implementations 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 = 5611937453792676565L;
042
043    //---------------------------------------------------------------Constants--
044    //--Implementations---------------------------------------------------------
045
046    /**
047     * The implementations held by the instance.
048     * @serial
049     */
050    private Implementation[] implementations;
051
052    /**
053     * Maps implementation identifiers to implementations.
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 Implementations} instance. */
065    public Implementations()
066    {
067        super();
068    }
069
070    /**
071     * Gets all implementations of the collection.
072     *
073     * @return all implementations of the collection.
074     */
075    public Implementation[] getImplementations()
076    {
077        if ( this.implementations == null )
078        {
079            this.implementations = new Implementation[ 0 ];
080            this.hashCode = 0;
081        }
082
083        return this.implementations;
084    }
085
086    /**
087     * Setter for property {@code implementations}.
088     *
089     * @param value the new implementations for the instance.
090     *
091     * @throws DuplicateImplementationException if {@code value} contains
092     * duplicate implementations.
093     */
094    public void setImplementations( final Implementation[] value )
095    {
096        this.identifiers.clear();
097        this.hashCode = 0;
098        this.implementations = 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
106                if ( this.identifiers.put( value[i].getIdentifier(),
107                                           value[i] ) != null )
108                {
109                    this.identifiers.clear();
110                    this.hashCode = 0;
111
112                    throw new DuplicateImplementationException(
113                        value[i].getIdentifier() );
114
115                }
116            }
117
118            this.implementations = value;
119        }
120    }
121
122    /**
123     * Gets an implementation for an identifier.
124     *
125     * @param identifier the identifier of the implementation to return.
126     *
127     * @return a reference to the implementation identified by
128     * {@code identifier}.
129     *
130     * @throws NullPointerException if {@code identifier} is {@code null}.
131     * @throws MissingImplementationException if no implementation matching
132     * {@code identifier} exists in the collection.
133     */
134    public Implementation getImplementation( final String identifier )
135    {
136        if ( identifier == null )
137        {
138            throw new NullPointerException( "identifier" );
139        }
140
141        final Implementation ret =
142            (Implementation) this.identifiers.get( identifier );
143
144        if ( ret == null )
145        {
146            throw new MissingImplementationException( identifier );
147        }
148
149        return ret;
150    }
151
152    /**
153     * Gets an implementation for an index.
154     *
155     * @param index the index of the implementation to return.
156     *
157     * @return a reference to the implementation at {@code index}.
158     *
159     * @throws IndexOutOfBoundsException if {@code index} is negativ,
160     * greater than or equal to {@code size()}.
161     */
162    public final Implementation getImplementation( final int index )
163    {
164        if ( index < 0 || index >= this.size() )
165        {
166            throw new ArrayIndexOutOfBoundsException( index );
167        }
168
169        return this.getImplementations()[index];
170    }
171
172    /**
173     * Gets the number of implementations held by the instance.
174     *
175     * @return the number of implementations held by the instance.
176     */
177    public final int size()
178    {
179        return this.getImplementations().length;
180    }
181
182    /**
183     * Creates a string representing the properties of the instance.
184     *
185     * @return a string representing the properties of the instance.
186     */
187    private String internalString()
188    {
189        final StringBuffer buf = new StringBuffer( 200 ).append( '{' );
190        buf.append( this.internalString( this ) );
191
192        final Implementation[] impls = this.getImplementations();
193        for ( int i = impls.length - 1; i >= 0; i-- )
194        {
195            buf.append( ", [" ).append( i ).append( "]=" ).
196                append( impls[i] );
197
198        }
199
200        buf.append( '}' );
201        return buf.toString();
202    }
203
204    //---------------------------------------------------------Implementations--
205    //--Object------------------------------------------------------------------
206
207    /**
208     * Indicates whether some other object is equal to this one by comparing
209     * the values of all properties.
210     *
211     * @param o the reference object with which to compare.
212     *
213     * @return {@code true} if this object is the same as {@code o};
214     * {@code false} otherwise.
215     */
216    public boolean equals( final Object o )
217    {
218        boolean equal = this == o;
219
220        if ( !equal && o instanceof Implementations )
221        {
222            final Implementations that = (Implementations) o;
223            final Collection these = Arrays.asList( this.getImplementations() );
224            final Collection those = Arrays.asList( that.getImplementations() );
225
226            equal = this.size() == that.size() && these.containsAll( those );
227        }
228
229        return equal;
230    }
231
232    /**
233     * Returns a hash code value for this object.
234     *
235     * @return a hash code value for this object.
236     */
237    public int hashCode()
238    {
239        return this.hashCode;
240    }
241
242    /**
243     * Returns a string representation of the object.
244     *
245     * @return a string representation of the object.
246     */
247    public String toString()
248    {
249        return super.toString() + this.internalString();
250    }
251
252    /**
253     * Creates and returns a deep copy of this object.
254     *
255     * @return a clone of this instance.
256     */
257    public Object clone()
258    {
259        try
260        {
261            final Implementations ret = (Implementations) super.clone();
262            final Implementation[] impls = this.getImplementations();
263            final Implementation[] cloned = new Implementation[ impls.length ];
264
265            for ( int i = impls.length - 1; i >= 0; i-- )
266            {
267                cloned[i] = (Implementation) impls[i].clone();
268            }
269
270            ret.setImplementations( cloned );
271            return ret;
272        }
273        catch ( final CloneNotSupportedException e )
274        {
275            throw new AssertionError( e );
276        }
277    }
278
279    //------------------------------------------------------------------Object--
280}