View Javadoc

1   /*
2    *  jDTAUS Core API
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.container;
22  
23  import java.io.Serializable;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.Map;
29  
30  /**
31   * Collection of modules.
32   *
33   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
34   * @version $JDTAUS: Modules.java 8743 2012-10-07 03:06:20Z schulte $
35   */
36  public class Modules extends ModelObject implements Cloneable, Serializable
37  {
38      //--Constants---------------------------------------------------------------
39  
40      /** Serial version UID for backwards compatibility with 1.0.x classes. */
41      private static final long serialVersionUID = 6139694129590900933L;
42  
43      //---------------------------------------------------------------Constants--
44      //--Modules-----------------------------------------------------------------
45  
46      /**
47       * The modules held by the instance.
48       * @serial
49       */
50      private Module[] modules;
51  
52      /**
53       * The specifications of all modules.
54       * @serial
55       * @deprecated The list of specifications is no longer cached. It needs
56       * to be computed on the fly to reflect changes of the model.
57       */
58      private Specifications specifications;
59  
60      /**
61       * The implementations of all modules.
62       * @serial
63       * @deprecated The list of implementations is no longer cached. It needs
64       * to be computed on the fly to reflect changes of the model.
65       */
66      private Implementations implementations;
67  
68      /**
69       * Maps module names to modules.
70       * @serial
71       */
72      private Map names = new HashMap( 1000 );
73  
74      /**
75       * Maps specification identifiers to specifications.
76       * @serial
77       */
78      private Map specificationMap = new HashMap( 1000 );
79  
80      /**
81       * Maps implementation identifiers to implementations.
82       * @serial
83       */
84      private Map implementationMap = new HashMap( 1000 );
85  
86      /**
87       * Hash code.
88       * @serial
89       */
90      private int hashCode;
91  
92      /** Creates a new {@code Modules} instance. */
93      public Modules()
94      {
95          super();
96      }
97  
98      /**
99       * Gets the modules of the collection.
100      *
101      * @return the modules of the collection.
102      */
103     public Module[] getModules()
104     {
105         if ( this.modules == null )
106         {
107             this.modules = new Module[ 0 ];
108             this.hashCode = 0;
109         }
110 
111         return this.modules;
112     }
113 
114     /**
115      * Setter for property {@code modules}.
116      *
117      * @param value the new collection of modules.
118      *
119      * @throws DuplicateModuleException if {@code value} contains duplicate
120      * modules.
121      * @throws DuplicateSpecificationException if {@code value} contains
122      * duplicate specifications.
123      * @throws DuplicateImplementationException if {@code value} contains
124      * duplicate implementations.
125      */
126     public void setModules( final Module[] value )
127     {
128         Specification spec;
129         Specifications specs;
130         Implementation impl;
131         Implementations impls;
132 
133         this.implementations = null;
134         this.specifications = null;
135         this.names.clear();
136         this.specificationMap.clear();
137         this.implementationMap.clear();
138         this.hashCode = 0;
139         this.modules = null;
140 
141         if ( value != null )
142         {
143             for ( int i = value.length - 1; i >= 0; i-- )
144             {
145                 this.hashCode += value[i].hashCode();
146 
147                 // Check module name uniqueness.
148                 if ( this.names.put( value[i].getName(), value[i] ) != null )
149                 {
150                     this.names.clear();
151                     this.specificationMap.clear();
152                     this.implementationMap.clear();
153                     this.hashCode = 0;
154 
155                     throw new DuplicateModuleException( value[i].getName() );
156                 }
157 
158                 // Check specification identifier uniqueness.
159                 specs = value[i].getSpecifications();
160                 for ( int j = specs.size() - 1; j >= 0; j-- )
161                 {
162                     spec = specs.getSpecification( j );
163                     if ( this.specificationMap.put(
164                         spec.getIdentifier(), spec ) != null )
165                     {
166                         this.names.clear();
167                         this.specificationMap.clear();
168                         this.implementationMap.clear();
169                         this.hashCode = 0;
170 
171                         throw new DuplicateSpecificationException(
172                             spec.getIdentifier() );
173 
174                     }
175                 }
176 
177                 // Check implementation identifier uniqueness.
178                 impls = value[i].getImplementations();
179                 for ( int j = impls.size() - 1; j >= 0; j-- )
180                 {
181                     impl = impls.getImplementation( j );
182                     if ( this.implementationMap.put(
183                         impl.getIdentifier(), impl ) != null )
184                     {
185                         this.names.clear();
186                         this.specificationMap.clear();
187                         this.implementationMap.clear();
188                         this.hashCode = 0;
189 
190                         throw new DuplicateImplementationException(
191                             impl.getIdentifier() );
192 
193                     }
194                 }
195             }
196 
197             this.modules = value;
198         }
199     }
200 
201     /**
202      * Gets a module for a name.
203      *
204      * @param name the name of the module to return.
205      *
206      * @return a reference to the module named {@code name}.
207      *
208      * @throws NullPointerException if {@code name} is {@code null}.
209      * @throws MissingModuleException if no module matching {@code name} exists
210      * in the collection.
211      */
212     public Module getModule( final String name )
213     {
214         if ( name == null )
215         {
216             throw new NullPointerException( "name" );
217         }
218 
219         final Module ret = (Module) this.names.get( name );
220 
221         if ( ret == null )
222         {
223             throw new MissingModuleException( name );
224         }
225 
226         return ret;
227     }
228 
229     /**
230      * Gets a module for an index.
231      *
232      * @param index the index of the module to return.
233      *
234      * @return a reference to the module at {@code index}.
235      *
236      * @throws IndexOutOfBoundsException if {@code index} is negativ,
237      * greater than or equal to {@code size()}.
238      */
239     public final Module getModule( final int index )
240     {
241         if ( index < 0 || index >= this.size() )
242         {
243             throw new ArrayIndexOutOfBoundsException( index );
244         }
245 
246         return this.getModules()[index];
247     }
248 
249     /**
250      * Gets a specification for an identifier.
251      *
252      * @param identifier the identifier of the specification to return.
253      *
254      * @return a reference to the specification identified by
255      * {@code identifier}.
256      *
257      * @throws NullPointerException if {@code identifier} is {@code null}.
258      * @throws MissingSpecificationException if no specification matching
259      * {@code identifier} exists.
260      */
261     public Specification getSpecification( final String identifier )
262     {
263         if ( identifier == null )
264         {
265             throw new NullPointerException( "identifier" );
266         }
267 
268         Specification ret =
269             (Specification) this.specificationMap.get( identifier );
270 
271         if ( ret == null )
272         {
273             // Check the instances for changes.
274             for ( int i = this.size() - 1; i >= 0 && ret == null; i-- )
275             {
276                 final Module mod = this.getModule( i );
277                 for ( int j = mod.getSpecifications().size() - 1; j >= 0; j-- )
278                 {
279                     final Specification spec =
280                         mod.getSpecifications().getSpecification( j );
281 
282                     if ( spec.getIdentifier().equals( identifier ) )
283                     {
284                         ret = spec;
285                         break;
286                     }
287                 }
288             }
289 
290             if ( ret == null )
291             {
292                 throw new MissingSpecificationException( identifier );
293             }
294         }
295 
296         return ret;
297     }
298 
299     /**
300      * Gets a collection of all specifications of all modules.
301      *
302      * @return a reference to all specifications of all modules held by the
303      * instance.
304      */
305     public Specifications getSpecifications()
306     {
307         if ( this.specifications == null )
308         {
309             this.specifications = new Specifications();
310         }
311 
312         final Collection col = new ArrayList( this.specifications.size() );
313 
314         for ( int i = this.size() - 1; i >= 0; i-- )
315         {
316             final Module mod = this.getModule( i );
317             for ( int j = mod.getSpecifications().size() - 1; j >= 0; j-- )
318             {
319                 col.add( mod.getSpecifications().getSpecification( j ) );
320             }
321         }
322 
323         this.specifications.setSpecifications(
324             (Specification[]) col.toArray( new Specification[ col.size() ] ) );
325 
326         return this.specifications;
327     }
328 
329     /**
330      * Gets an implementation for an identifier.
331      *
332      * @param identifier the identifier of the implementation to return.
333      *
334      * @return a reference to the implementation identified by
335      * {@code identifier}.
336      *
337      * @throws NullPointerException if {@code identifier} is {@code null}.
338      * @throws MissingImplementationException if no implementation matching
339      * {@code identifier} exists.
340      */
341     public Implementation getImplementation( final String identifier )
342     {
343         if ( identifier == null )
344         {
345             throw new NullPointerException( "identifier" );
346         }
347 
348         Implementation ret =
349             (Implementation) this.implementationMap.get( identifier );
350 
351         if ( ret == null )
352         {
353             // Check the instances for changes.
354             for ( int i = this.size() - 1; i >= 0 && ret == null; i-- )
355             {
356                 final Module mod = this.getModule( i );
357                 for ( int j = mod.getImplementations().size() - 1; j >= 0; j-- )
358                 {
359                     final Implementation impl =
360                         mod.getImplementations().getImplementation( j );
361 
362                     if ( impl.getIdentifier().equals( identifier ) )
363                     {
364                         ret = impl;
365                         break;
366                     }
367                 }
368             }
369 
370             if ( ret == null )
371             {
372                 throw new MissingImplementationException( identifier );
373             }
374         }
375 
376         return ret;
377     }
378 
379     /**
380      * Gets a collection of all implementations of all modules held by the
381      * instance.
382      *
383      * @return a reference to all implementations of all modules held by the
384      * instance.
385      */
386     public Implementations getImplementations()
387     {
388         if ( this.implementations == null )
389         {
390             this.implementations = new Implementations();
391         }
392 
393         final Collection col = new ArrayList( this.implementations.size() );
394 
395         for ( int i = this.size() - 1; i >= 0; i-- )
396         {
397             final Module mod = this.getModule( i );
398             for ( int j = mod.getImplementations().size() - 1; j >= 0; j-- )
399             {
400                 col.add( mod.getImplementations().getImplementation( j ) );
401             }
402         }
403 
404         this.implementations.setImplementations(
405             (Implementation[]) col.toArray(
406             new Implementation[ col.size() ] ) );
407 
408         return this.implementations;
409     }
410 
411     /**
412      * Gets the number of modules held by the instance.
413      *
414      * @return the number of modules held by the instance.
415      */
416     public final int size()
417     {
418         return this.getModules().length;
419     }
420 
421     /**
422      * Creates a string representing the properties of the instance.
423      *
424      * @return a string representing the properties of the instance.
425      */
426     private String internalString()
427     {
428         final StringBuffer buf = new StringBuffer( 200 ).append( '{' );
429         buf.append( this.internalString( this ) );
430 
431         final Module[] mods = this.getModules();
432         for ( int i = mods.length - 1; i >= 0; i-- )
433         {
434             buf.append( ", [" ).append( i ).append( "]=" ).
435                 append( mods[i] );
436 
437         }
438 
439         buf.append( '}' );
440         return buf.toString();
441     }
442 
443     //-----------------------------------------------------------------Modules--
444     //--Object------------------------------------------------------------------
445 
446     /**
447      * Indicates whether some other object is equal to this one by comparing
448      * the values of all properties.
449      *
450      * @param o the reference object with which to compare.
451      *
452      * @return {@code true} if this object is the same as {@code o};
453      * {@code false} otherwise.
454      */
455     public boolean equals( final Object o )
456     {
457         boolean equal = this == o;
458 
459         if ( !equal && o instanceof Modules )
460         {
461             final Modules that = (Modules) o;
462             final Collection these = Arrays.asList( this.getModules() );
463             final Collection those = Arrays.asList( that.getModules() );
464 
465             equal = this.size() == that.size() && these.containsAll( those );
466         }
467 
468         return equal;
469     }
470 
471     /**
472      * Returns a hash code value for this object.
473      *
474      * @return a hash code value for this object.
475      */
476     public int hashCode()
477     {
478         return this.hashCode;
479     }
480 
481     /**
482      * Returns a string representation of the object.
483      *
484      * @return a string representation of the object.
485      */
486     public String toString()
487     {
488         return super.toString() + this.internalString();
489     }
490 
491     /**
492      * Creates and returns a deep copy of this object.
493      *
494      * @return a clone of this instance.
495      */
496     public Object clone()
497     {
498         try
499         {
500             final Modules ret = (Modules) super.clone();
501             final Module[] mods = this.getModules();
502             final Module[] cloned = new Module[ mods.length ];
503 
504             for ( int i = mods.length - 1; i >= 0; i-- )
505             {
506                 cloned[i] = (Module) mods[i].clone();
507             }
508 
509             ret.setModules( cloned );
510             return ret;
511         }
512         catch ( final CloneNotSupportedException e )
513         {
514             throw new AssertionError( e );
515         }
516     }
517 
518     //------------------------------------------------------------------Object--
519 }