001/* 002 * jDTAUS Core API 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.container; 022 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.HashMap; 028import java.util.Map; 029 030/** 031 * Collection of modules. 032 * 033 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 034 * @version $JDTAUS: Modules.java 8743 2012-10-07 03:06:20Z schulte $ 035 */ 036public class Modules extends ModelObject implements Cloneable, Serializable 037{ 038 //--Constants--------------------------------------------------------------- 039 040 /** Serial version UID for backwards compatibility with 1.0.x classes. */ 041 private static final long serialVersionUID = 6139694129590900933L; 042 043 //---------------------------------------------------------------Constants-- 044 //--Modules----------------------------------------------------------------- 045 046 /** 047 * The modules held by the instance. 048 * @serial 049 */ 050 private Module[] modules; 051 052 /** 053 * The specifications of all modules. 054 * @serial 055 * @deprecated The list of specifications is no longer cached. It needs 056 * to be computed on the fly to reflect changes of the model. 057 */ 058 private Specifications specifications; 059 060 /** 061 * The implementations of all modules. 062 * @serial 063 * @deprecated The list of implementations is no longer cached. It needs 064 * to be computed on the fly to reflect changes of the model. 065 */ 066 private Implementations implementations; 067 068 /** 069 * Maps module names to modules. 070 * @serial 071 */ 072 private Map names = new HashMap( 1000 ); 073 074 /** 075 * Maps specification identifiers to specifications. 076 * @serial 077 */ 078 private Map specificationMap = new HashMap( 1000 ); 079 080 /** 081 * Maps implementation identifiers to implementations. 082 * @serial 083 */ 084 private Map implementationMap = new HashMap( 1000 ); 085 086 /** 087 * Hash code. 088 * @serial 089 */ 090 private int hashCode; 091 092 /** Creates a new {@code Modules} instance. */ 093 public Modules() 094 { 095 super(); 096 } 097 098 /** 099 * 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}