001    /*
002     *  jDTAUS - DTAUS fileformat.
003     *  Copyright (c) 2005 Christian Schulte <cs@schulte.it>
004     *
005     *  This library is free software; you can redistribute it and/or
006     *  modify it under the terms of the GNU Lesser General Public
007     *  License as published by the Free Software Foundation; either
008     *  version 2.1 of the License, or any later version.
009     *
010     *  This library is distributed in the hope that it will be useful,
011     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
012     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     *  Lesser General Public License for more details.
014     *
015     *  You should have received a copy of the GNU Lesser General Public
016     *  License along with this library; if not, write to the Free Software
017     *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
018     *
019     */
020    package org.jdtaus.core.container;
021    
022    import java.io.Serializable;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Collection;
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.Map;
029    
030    /**
031     * Collection of modules.
032     *
033     * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
034     * @version $Id: Modules.java 2230 2007-03-26 01:37:48Z schulte2005 $
035     */
036    public class Modules implements Cloneable, Serializable
037    {
038    
039        //--Modules-----------------------------------------------------------------
040    
041        /**
042         * The modules held by the instance.
043         * @serial
044         */
045        private Module[] modules;
046    
047        /**
048         * The specifications of all modules.
049         * @serial
050         */
051        private Specifications specifications;
052    
053        /**
054         * The implementations of all modules.
055         * @serial
056         */
057        private Implementations implementations;
058    
059        /**
060         * Maps module names to modules.
061         * @serial
062         */
063        private Map names = new HashMap(1000);
064    
065        /**
066         * Maps specification identifiers to specifications.
067         * @serial
068         */
069        private Map specificationMap = new HashMap(1000);
070    
071        /**
072         * Maps implementation identifiers to implementations.
073         * @serial
074         */
075        private Map implementationMap = new HashMap(1000);
076    
077        /**
078         * Hash code.
079         * @serial
080         */
081        private int hashCode;
082    
083        /**
084         * Gets the modules of the collection.
085         *
086         * @return the modules of the collection.
087         */
088        public Module[] getModules()
089        {
090            if(this.modules == null)
091            {
092                this.modules = new Module[0];
093                this.hashCode = 0;
094            }
095    
096            return this.modules;
097        }
098    
099        /**
100         * Setter for property {@code modules}.
101         *
102         * @param value the new collection of modules.
103         *
104         * @throws DuplicateModuleException if {@code value} contains duplicate
105         * modules.
106         * @throws DuplicateSpecificationException if {@code value} contains
107         * duplicate specifications.
108         * @throws DuplicateImplementationException if {@code value} contains
109         * duplicate implementations.
110         */
111        public void setModules(final Module[] value)
112        {
113            Specification spec;
114            Specifications specs;
115            Implementation impl;
116            Implementations impls;
117    
118            this.implementations = null;
119            this.specifications = null;
120            this.names.clear();
121            this.specificationMap.clear();
122            this.implementationMap.clear();
123            this.hashCode = 0;
124            this.modules = value;
125    
126            if(value != null)
127            {
128                for(int i = value.length - 1; i >= 0; i--)
129                {
130                    this.hashCode += value[i].hashCode();
131    
132                    // Check module name uniqueness.
133                    if(this.names.put(value[i].getName(), value[i]) != null)
134                    {
135                        throw new DuplicateModuleException(value[i].getName());
136                    }
137    
138                    // Check specification identifier uniqueness.
139                    specs = value[i].getSpecifications();
140                    for(int j = specs.size() - 1; j >= 0; j--)
141                    {
142                        spec = specs.getSpecification(j);
143                        if(this.specificationMap.
144                            put(spec.getIdentifier(), spec) != null)
145                        {
146    
147                            throw new DuplicateSpecificationException(
148                                spec.getIdentifier());
149    
150                        }
151                    }
152    
153                    // Check implementation identifier uniqueness.
154                    impls = value[i].getImplementations();
155                    for(int j = impls.size() - 1; j >= 0; j--)
156                    {
157                        impl = impls.getImplementation(j);
158                        if(this.implementationMap.
159                            put(impl.getIdentifier(), impl) != null)
160                        {
161    
162                            throw new DuplicateImplementationException(
163                                impl.getIdentifier());
164    
165                        }
166                    }
167                }
168            }
169        }
170    
171        /**
172         * Gets a module for a name.
173         *
174         * @param name the name of the module to return.
175         *
176         * @return a reference to the module named {@code name}.
177         *
178         * @throws NullPointerException if {@code name} is {@code null}.
179         * @throws MissingModuleException if no module matching {@code name} exists
180         * in the collection.
181         */
182        public Module getModule(final String name)
183        {
184    
185            if(name == null)
186            {
187                throw new NullPointerException("name");
188            }
189    
190            final Module ret = (Module) this.names.get(name);
191    
192            if(ret == null)
193            {
194                throw new MissingModuleException(name);
195            }
196    
197            return ret;
198        }
199    
200        /**
201         * Gets a module for an index.
202         *
203         * @param index the index of the module to return.
204         *
205         * @return a reference to the module at {@code index}.
206         *
207         * @throws IndexOutOfBoundsException if {@code index} is negativ,
208         * greater than or equal to {@code size()}.
209         */
210        public final Module getModule(final int index)
211        {
212            if(index < 0 || index >= this.size())
213            {
214                throw new ArrayIndexOutOfBoundsException(index);
215            }
216    
217            return this.getModules()[index];
218        }
219    
220        /**
221         * Gets a specification for an identifier.
222         *
223         * @param identifier the identifier of the specification to return.
224         *
225         * @return a reference to the specification identified by
226         * {@code identifier}.
227         *
228         * @throws NullPointerException if {@code identifier} is {@code null}.
229         * @throws MissingSpecificationException if no specification matching
230         * {@code identifier} exists.
231         */
232        public Specification getSpecification(final String identifier)
233        {
234    
235            if(identifier == null)
236            {
237                throw new NullPointerException("identifier");
238            }
239    
240            final Specification ret =
241                (Specification) this.specificationMap.get(identifier);
242    
243            if(ret == null)
244            {
245                throw new MissingSpecificationException(identifier);
246            }
247    
248            return ret;
249        }
250    
251        /**
252         * Gets a collection of all specifications of all modules.
253         *
254         * @return a reference to all specifications of all modules held by the
255         * instance.
256         */
257        public Specifications getSpecifications()
258        {
259            if(this.specifications == null)
260            {
261                final Iterator it;
262                final Collection specs =
263                    new ArrayList(this.specificationMap.size());
264    
265                this.specifications = new Specifications();
266                for(it = this.specificationMap.keySet().iterator(); it.hasNext();)
267                {
268                    specs.add(this.specificationMap.get(it.next()));
269                }
270                this.specifications.setSpecifications((Specification[]) specs.
271                    toArray(new Specification[specs.size()]));
272    
273            }
274    
275            return this.specifications;
276        }
277    
278        /**
279         * Gets an implementation for an identifier.
280         *
281         * @param identifier the identifier of the implementation to return.
282         *
283         * @return a reference to the implementation identified by
284         * {@code identifier}.
285         *
286         * @throws NullPointerException if {@code identifier} is {@code null}.
287         * @throws MissingImplementationException if no implementation matching
288         * {@code identifier} exists.
289         */
290        public Implementation getImplementation(final String identifier)
291        {
292    
293            if(identifier == null)
294            {
295                throw new NullPointerException("identifier");
296            }
297    
298            final Implementation ret =
299                (Implementation) this.implementationMap.get(identifier);
300    
301            if(ret == null)
302            {
303                throw new MissingImplementationException(identifier);
304            }
305    
306            return ret;
307        }
308    
309        /**
310         * Gets a collection of all implementations of all modules held by the
311         * instance.
312         *
313         * @return a reference to all implementations of all modules held by the
314         * instance.
315         */
316        public Implementations getImplementations()
317        {
318            if(this.implementations == null)
319            {
320                final Iterator it;
321                final Collection impls =
322                    new ArrayList(this.implementationMap.size());
323    
324                this.implementations = new Implementations();
325                for(it = this.implementationMap.keySet().
326                    iterator(); it.hasNext();)
327                {
328    
329                    impls.add(this.implementationMap.get(it.next()));
330                }
331    
332                this.implementations.setImplementations((Implementation[]) impls.
333                    toArray(new Implementation[impls.size()]));
334    
335            }
336    
337            return this.implementations;
338        }
339    
340        /**
341         * Gets the number of modules held by the instance.
342         *
343         * @return the number of modules held by the instance.
344         */
345        public final int size()
346        {
347            return this.getModules().length;
348        }
349    
350        /**
351         * Creates a string representing the properties of the instance.
352         *
353         * @return a string representing the properties of the instance.
354         */
355        private String internalString()
356        {
357            final StringBuffer buf = new StringBuffer(200);
358            final Module[] modules = this.getModules();
359            for(int i = modules.length - 1; i >= 0; i--)
360            {
361                buf.append("\n\tmodule[").append(i).append("]=").
362                    append(modules[i]);
363    
364            }
365    
366            return buf.toString();
367        }
368    
369        //-----------------------------------------------------------------Modules--
370        //--Object------------------------------------------------------------------
371    
372        /**
373         * Indicates whether some other object is equal to this one by comparing
374         * the values of all properties.
375         *
376         * @param o the reference object with which to compare.
377         *
378         * @return {@code true} if this object is the same as {@code o};
379         * {@code false} otherwise.
380         */
381        public boolean equals(final Object o)
382        {
383            boolean equal = this == o;
384    
385            if(!equal && o instanceof Modules)
386            {
387                final Modules that = (Modules) o;
388                final Collection these = Arrays.asList(this.getModules());
389                final Collection those = Arrays.asList(that.getModules());
390    
391                equal = these.size() == that.size() && these.containsAll(those);
392            }
393    
394            return equal;
395        }
396    
397        /**
398         * Returns a hash code value for this object.
399         *
400         * @return a hash code value for this object.
401         */
402        public int hashCode()
403        {
404            return this.hashCode;
405        }
406    
407        /**
408         * Returns a string representation of the object.
409         *
410         * @return a string representation of the object.
411         */
412        public String toString()
413        {
414            return super.toString() + this.internalString();
415        }
416    
417        /**
418         * Creates and returns a deep copy of this object.
419         *
420         * @return a clone of this instance.
421         */
422        public Object clone()
423        {
424            try
425            {
426                final Modules ret = (Modules) super.clone();
427                final Module[] mods = this.getModules();
428                final Module[] cloned = new Module[mods.length];
429    
430                for(int i = mods.length - 1; i >= 0; i--)
431                {
432                    cloned[i] = (Module) mods[i].clone();
433                }
434    
435                ret.setModules(cloned);
436                return ret;
437            }
438            catch(CloneNotSupportedException e)
439            {
440                throw new AssertionError(e);
441            }
442        }
443    
444        //------------------------------------------------------------------Object--
445    
446    }