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 }