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