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.ArrayList;
027    import java.util.Arrays;
028    import java.util.Collection;
029    import java.util.HashMap;
030    import java.util.Map;
031    
032    /**
033     * Collection of modules.
034     *
035     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
036     * @version $Id: Modules.java 8044 2009-07-02 01:29:05Z schulte2005 $
037     */
038    public class Modules extends ModelObject 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 = 6139694129590900933L;
044    
045        //---------------------------------------------------------------Constants--
046        //--Modules-----------------------------------------------------------------
047    
048        /**
049         * The modules held by the instance.
050         * @serial
051         */
052        private Module[] modules;
053    
054        /**
055         * The specifications of all modules.
056         * @serial
057         * @deprecated The list of specifications is no longer cached. It needs
058         * to be computed on the fly to reflect changes of the model.
059         */
060        private Specifications specifications;
061    
062        /**
063         * The implementations of all modules.
064         * @serial
065         * @deprecated The list of implementations is no longer cached. It needs
066         * to be computed on the fly to reflect changes of the model.
067         */
068        private Implementations implementations;
069    
070        /**
071         * Maps module names to modules.
072         * @serial
073         */
074        private Map names = new HashMap( 1000 );
075    
076        /**
077         * Maps specification identifiers to specifications.
078         * @serial
079         */
080        private Map specificationMap = new HashMap( 1000 );
081    
082        /**
083         * Maps implementation identifiers to implementations.
084         * @serial
085         */
086        private Map implementationMap = new HashMap( 1000 );
087    
088        /**
089         * Hash code.
090         * @serial
091         */
092        private int hashCode;
093    
094        /**
095         * Gets the modules of the collection.
096         *
097         * @return the modules of the collection.
098         */
099        public Module[] getModules()
100        {
101            if ( this.modules == null )
102            {
103                this.modules = new Module[ 0 ];
104                this.hashCode = 0;
105            }
106    
107            return this.modules;
108        }
109    
110        /**
111         * Setter for property {@code modules}.
112         *
113         * @param value the new collection of modules.
114         *
115         * @throws DuplicateModuleException if {@code value} contains duplicate
116         * modules.
117         * @throws DuplicateSpecificationException if {@code value} contains
118         * duplicate specifications.
119         * @throws DuplicateImplementationException if {@code value} contains
120         * duplicate implementations.
121         */
122        public void setModules( final Module[] value )
123        {
124            Specification spec;
125            Specifications specs;
126            Implementation impl;
127            Implementations impls;
128    
129            this.implementations = null;
130            this.specifications = null;
131            this.names.clear();
132            this.specificationMap.clear();
133            this.implementationMap.clear();
134            this.hashCode = 0;
135            this.modules = null;
136    
137            if ( value != null )
138            {
139                for ( int i = value.length - 1; i >= 0; i-- )
140                {
141                    this.hashCode += value[i].hashCode();
142    
143                    // Check module name uniqueness.
144                    if ( this.names.put( value[i].getName(), value[i] ) != null )
145                    {
146                        this.names.clear();
147                        this.specificationMap.clear();
148                        this.implementationMap.clear();
149                        this.hashCode = 0;
150    
151                        throw new DuplicateModuleException( value[i].getName() );
152                    }
153    
154                    // Check specification identifier uniqueness.
155                    specs = value[i].getSpecifications();
156                    for ( int j = specs.size() - 1; j >= 0; j-- )
157                    {
158                        spec = specs.getSpecification( j );
159                        if ( this.specificationMap.put(
160                            spec.getIdentifier(), spec ) != null )
161                        {
162                            this.names.clear();
163                            this.specificationMap.clear();
164                            this.implementationMap.clear();
165                            this.hashCode = 0;
166    
167                            throw new DuplicateSpecificationException(
168                                spec.getIdentifier() );
169    
170                        }
171                    }
172    
173                    // Check implementation identifier uniqueness.
174                    impls = value[i].getImplementations();
175                    for ( int j = impls.size() - 1; j >= 0; j-- )
176                    {
177                        impl = impls.getImplementation( j );
178                        if ( this.implementationMap.put(
179                            impl.getIdentifier(), impl ) != null )
180                        {
181                            this.names.clear();
182                            this.specificationMap.clear();
183                            this.implementationMap.clear();
184                            this.hashCode = 0;
185    
186                            throw new DuplicateImplementationException(
187                                impl.getIdentifier() );
188    
189                        }
190                    }
191                }
192    
193                this.modules = value;
194            }
195        }
196    
197        /**
198         * Gets a module for a name.
199         *
200         * @param name the name of the module to return.
201         *
202         * @return a reference to the module named {@code name}.
203         *
204         * @throws NullPointerException if {@code name} is {@code null}.
205         * @throws MissingModuleException if no module matching {@code name} exists
206         * in the collection.
207         */
208        public Module getModule( final String name )
209        {
210            if ( name == null )
211            {
212                throw new NullPointerException( "name" );
213            }
214    
215            final Module ret = (Module) this.names.get( name );
216    
217            if ( ret == null )
218            {
219                throw new MissingModuleException( name );
220            }
221    
222            return ret;
223        }
224    
225        /**
226         * Gets a module for an index.
227         *
228         * @param index the index of the module to return.
229         *
230         * @return a reference to the module at {@code index}.
231         *
232         * @throws IndexOutOfBoundsException if {@code index} is negativ,
233         * greater than or equal to {@code size()}.
234         */
235        public final Module getModule( final int index )
236        {
237            if ( index < 0 || index >= this.size() )
238            {
239                throw new ArrayIndexOutOfBoundsException( index );
240            }
241    
242            return this.getModules()[index];
243        }
244    
245        /**
246         * Gets a specification for an identifier.
247         *
248         * @param identifier the identifier of the specification to return.
249         *
250         * @return a reference to the specification identified by
251         * {@code identifier}.
252         *
253         * @throws NullPointerException if {@code identifier} is {@code null}.
254         * @throws MissingSpecificationException if no specification matching
255         * {@code identifier} exists.
256         */
257        public Specification getSpecification( final String identifier )
258        {
259            if ( identifier == null )
260            {
261                throw new NullPointerException( "identifier" );
262            }
263    
264            Specification ret =
265                (Specification) this.specificationMap.get( identifier );
266    
267            if ( ret == null )
268            {
269                // Check the instances for changes.
270                for ( int i = this.size() - 1; i >= 0 && ret == null; i-- )
271                {
272                    final Module mod = this.getModule( i );
273                    for ( int j = mod.getSpecifications().size() - 1; j >= 0; j-- )
274                    {
275                        final Specification spec =
276                            mod.getSpecifications().getSpecification( j );
277    
278                        if ( spec.getIdentifier().equals( identifier ) )
279                        {
280                            ret = spec;
281                            break;
282                        }
283                    }
284                }
285    
286                if ( ret == null )
287                {
288                    throw new MissingSpecificationException( identifier );
289                }
290            }
291    
292            return ret;
293        }
294    
295        /**
296         * Gets a collection of all specifications of all modules.
297         *
298         * @return a reference to all specifications of all modules held by the
299         * instance.
300         */
301        public Specifications getSpecifications()
302        {
303            if ( this.specifications == null )
304            {
305                this.specifications = new Specifications();
306            }
307    
308            final Collection col = new ArrayList( this.specifications.size() );
309    
310            for ( int i = this.size() - 1; i >= 0; i-- )
311            {
312                final Module mod = this.getModule( i );
313                for ( int j = mod.getSpecifications().size() - 1; j >= 0; j-- )
314                {
315                    col.add( mod.getSpecifications().getSpecification( j ) );
316                }
317            }
318    
319            this.specifications.setSpecifications(
320                (Specification[]) col.toArray( new Specification[ col.size() ] ) );
321    
322            return this.specifications;
323        }
324    
325        /**
326         * Gets an implementation for an identifier.
327         *
328         * @param identifier the identifier of the implementation to return.
329         *
330         * @return a reference to the implementation identified by
331         * {@code identifier}.
332         *
333         * @throws NullPointerException if {@code identifier} is {@code null}.
334         * @throws MissingImplementationException if no implementation matching
335         * {@code identifier} exists.
336         */
337        public Implementation getImplementation( final String identifier )
338        {
339            if ( identifier == null )
340            {
341                throw new NullPointerException( "identifier" );
342            }
343    
344            Implementation ret =
345                (Implementation) this.implementationMap.get( identifier );
346    
347            if ( ret == null )
348            {
349                // Check the instances for changes.
350                for ( int i = this.size() - 1; i >= 0 && ret == null; i-- )
351                {
352                    final Module mod = this.getModule( i );
353                    for ( int j = mod.getImplementations().size() - 1; j >= 0; j-- )
354                    {
355                        final Implementation impl =
356                            mod.getImplementations().getImplementation( j );
357    
358                        if ( impl.getIdentifier().equals( identifier ) )
359                        {
360                            ret = impl;
361                            break;
362                        }
363                    }
364                }
365    
366                if ( ret == null )
367                {
368                    throw new MissingImplementationException( identifier );
369                }
370            }
371    
372            return ret;
373        }
374    
375        /**
376         * Gets a collection of all implementations of all modules held by the
377         * instance.
378         *
379         * @return a reference to all implementations of all modules held by the
380         * instance.
381         */
382        public Implementations getImplementations()
383        {
384            if ( this.implementations == null )
385            {
386                this.implementations = new Implementations();
387            }
388    
389            final Collection col = new ArrayList( this.implementations.size() );
390    
391            for ( int i = this.size() - 1; i >= 0; i-- )
392            {
393                final Module mod = this.getModule( i );
394                for ( int j = mod.getImplementations().size() - 1; j >= 0; j-- )
395                {
396                    col.add( mod.getImplementations().getImplementation( j ) );
397                }
398            }
399    
400            this.implementations.setImplementations(
401                (Implementation[]) col.toArray(
402                new Implementation[ col.size() ] ) );
403    
404            return this.implementations;
405        }
406    
407        /**
408         * Gets the number of modules held by the instance.
409         *
410         * @return the number of modules held by the instance.
411         */
412        public final int size()
413        {
414            return this.getModules().length;
415        }
416    
417        /**
418         * Creates a string representing the properties of the instance.
419         *
420         * @return a string representing the properties of the instance.
421         */
422        private String internalString()
423        {
424            final StringBuffer buf = new StringBuffer( 200 ).append( '{' );
425            buf.append( this.internalString( this ) );
426    
427            final Module[] mods = this.getModules();
428            for ( int i = mods.length - 1; i >= 0; i-- )
429            {
430                buf.append( ", [" ).append( i ).append( "]=" ).
431                    append( mods[i] );
432    
433            }
434    
435            buf.append( '}' );
436            return buf.toString();
437        }
438    
439        //-----------------------------------------------------------------Modules--
440        //--Object------------------------------------------------------------------
441    
442        /**
443         * Indicates whether some other object is equal to this one by comparing
444         * the values of all properties.
445         *
446         * @param o the reference object with which to compare.
447         *
448         * @return {@code true} if this object is the same as {@code o};
449         * {@code false} otherwise.
450         */
451        public boolean equals( final Object o )
452        {
453            boolean equal = this == o;
454    
455            if ( !equal && o instanceof Modules )
456            {
457                final Modules that = (Modules) o;
458                final Collection these = Arrays.asList( this.getModules() );
459                final Collection those = Arrays.asList( that.getModules() );
460    
461                equal = this.size() == that.size() && these.containsAll( those );
462            }
463    
464            return equal;
465        }
466    
467        /**
468         * Returns a hash code value for this object.
469         *
470         * @return a hash code value for this object.
471         */
472        public int hashCode()
473        {
474            return this.hashCode;
475        }
476    
477        /**
478         * Returns a string representation of the object.
479         *
480         * @return a string representation of the object.
481         */
482        public String toString()
483        {
484            return super.toString() + this.internalString();
485        }
486    
487        /**
488         * Creates and returns a deep copy of this object.
489         *
490         * @return a clone of this instance.
491         */
492        public Object clone()
493        {
494            try
495            {
496                final Modules ret = (Modules) super.clone();
497                final Module[] mods = this.getModules();
498                final Module[] cloned = new Module[ mods.length ];
499    
500                for ( int i = mods.length - 1; i >= 0; i-- )
501                {
502                    cloned[i] = (Module) mods[i].clone();
503                }
504    
505                ret.setModules( cloned );
506                return ret;
507            }
508            catch ( CloneNotSupportedException e )
509            {
510                throw new AssertionError( e );
511            }
512        }
513    
514        //------------------------------------------------------------------Object--
515    }