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 | } |