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 }