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.ObjectStreamException;
026 import java.io.Serializable;
027 import java.util.Map;
028 import java.util.TreeMap;
029
030 /**
031 * Specification meta-data.
032 * <p>A specification consists of the properties {@code identifier},
033 * {@code vendor}, {@code description} and {@code version}. Property
034 * {@code identifier} holds an identifier uniquely identifying the specification
035 * in a set of specifications. Property {@code vendor} holds vendor information
036 * for the vendor providing the specification. Property {@code description}
037 * holds a textual description and property {@code version} holds a textual
038 * version of the specification. The {@code stateless} flag indicates that state
039 * does not need to be retained across operations for instances to operate as
040 * specified. Property {@code multiplicity} specifies the number of
041 * implementations allowed to exist among a set of modules. A specification with
042 * {@code MULTIPLICITY_ONE} specifies that exactly one implementation of the
043 * specification must exist among a set of modules. A specification with
044 * {@code MULTIPLICITY_MANY} specifies that multiple implementations of the
045 * specification are allowed to exist among a set of modules (including none).
046 * Property {@code scope} specifies the scope the specification applies to.
047 * In multiton scope, a new instance is created whenever requested. In context
048 * scope, instances are bound to a system's context. An instance is only created
049 * if not already available in context. In singleton scope, instances are bound
050 * to a system's single instance store. An instance is only created if not
051 * already available in that single instance store.</p>
052 *
053 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
054 * @version $Id: Specification.java 8044 2009-07-02 01:29:05Z schulte2005 $
055 */
056 public class Specification extends ModelObject
057 implements Cloneable, Serializable
058 {
059 //--Constants---------------------------------------------------------------
060
061 /**
062 * Constant for property {@code multiplicity}.
063 * <p>A specification with {@code MULTIPLICITY_ONE} specifies that exactly
064 * one implementation of the specification must exist among a set of
065 * modules.</p>
066 */
067 public static final int MULTIPLICITY_ONE = 25000;
068
069 /**
070 * Constant for property {@code multiplicity}.
071 * <p>A specification with {@code MULTIPLICITY_MANY} specifies that multiple
072 * implementations of the specification are allowed to exist among a set of
073 * modules (including none).</p>
074 */
075 public static final int MULTIPLICITY_MANY = 25001;
076
077 /**
078 * Constant for property {@code scope}.
079 * <p>In multiton scope, a new instance is created whenever requested.</p>
080 */
081 public static final int SCOPE_MULTITON = 26000;
082
083 /**
084 * Constant for property {@code scope}.
085 * <p>In context scope, instances are bound to a system's context. An
086 * instance is only created if not already available in context.</p>
087 */
088 public static final int SCOPE_CONTEXT = 26001;
089
090 /**
091 * Constant for property {@code scope}.
092 * <p>In singleton scope, instances are bound to a system's single instance
093 * store. An instance is only created if not already available in that
094 * single instance store.</p>
095 */
096 public static final int SCOPE_SINGLETON = 26002;
097
098 /** Serial version UID for backwards compatibility with 1.0.x classes. */
099 private static final long serialVersionUID = -1829249262406961967L;
100
101 //---------------------------------------------------------------Constants--
102 //--Specification-----------------------------------------------------------
103
104 /**
105 * The name of the module holding the specification.
106 * @serial
107 */
108 private String moduleName;
109
110 /**
111 * The description of the specification.
112 * @serial
113 * @deprecated Replaced by property {@code documentation}.
114 */
115 private String description;
116
117 /**
118 * The identifier of the specification.
119 * @serial
120 */
121 private String identifier;
122
123 /**
124 * The flag indicating that instances of implementations of the
125 * specification should be created using a singleton strategy.
126 * @serial
127 * @deprecated Replaced by {@link #scope}.
128 */
129 private boolean singleton;
130
131 /**
132 * The vendor of the specification.
133 * @serial
134 */
135 private String vendor;
136
137 /**
138 * The version of the specification.
139 * @serial
140 */
141 private String version;
142
143 /**
144 * The implementation multiplicity of the specification.
145 * @serial
146 */
147 private int multiplicity = MULTIPLICITY_MANY;
148
149 /**
150 * The scope the specification applies to.
151 * @serial
152 */
153 private int scope = SCOPE_MULTITON;
154
155 /**
156 * The flag indicating if state need not be retained across method
157 * invocations for implementations to operate as specified.
158 * @serial
159 */
160 private boolean stateless;
161
162 /**
163 * The implementations available for the specification.
164 * @serial
165 */
166 private Implementations implementations;
167
168 /**
169 * Maps implementation names to implementations.
170 * @serial
171 */
172 private final Map implementationNames = new TreeMap();
173
174 /**
175 * The properties of the specification.
176 * @serial
177 */
178 private Properties properties;
179
180 /**
181 * Gets the name of the module holding the specification.
182 *
183 * @return the name of the module holding the specification.
184 */
185 public String getModuleName()
186 {
187 if ( this.moduleName == null )
188 {
189 this.moduleName = "";
190 }
191
192 return this.moduleName;
193 }
194
195 /**
196 * Setter for property {@code moduleName}.
197 *
198 * @param value the new name of the module holding the specification.
199 */
200 public void setModuleName( final String value )
201 {
202 this.moduleName = value;
203 }
204
205 /**
206 * Gets the description of the specification.
207 *
208 * @return the description of the specification or {@code null}.
209 *
210 * @deprecated Replaced by {@link #getDocumentation() getDocumentation().getValue()}.
211 */
212 public String getDescription()
213 {
214 return this.getDocumentation().getValue();
215 }
216
217 /**
218 * Setter for property {@code description}.
219 *
220 * @param value the new description of the specification.
221 * @deprecated Replaced by {@link #getDocumentation() getDocumentation().setValue(value)}.
222 */
223 public void setDescription( final String value )
224 {
225 this.getDocumentation().setValue( value );
226 }
227
228 /**
229 * Gets the identifier of the specification.
230 *
231 * @return the unique identifier of the specification.
232 */
233 public String getIdentifier()
234 {
235 if ( this.identifier == null )
236 {
237 this.identifier = "";
238 }
239
240 return this.identifier;
241 }
242
243 /**
244 * Setter for property {@code identifier}.
245 *
246 * @param value the new identifier of the specification.
247 */
248 public void setIdentifier( final String value )
249 {
250 this.identifier = value;
251 }
252
253 /**
254 * Gets the flag indicating the instantiation strategy of the specification.
255 *
256 * @return {@code true} if the specification is specifying a singleton;
257 * {@code false} if not.
258 *
259 * @see PropertyOverwriteConstraintException
260 * @deprecated Replaced by {@link #getScope() getScope() == SCOPE_SINGLETON}.
261 */
262 public boolean isSingleton()
263 {
264 return this.getScope() == SCOPE_SINGLETON;
265 }
266
267 /**
268 * Setter for property {@code singleton}.
269 *
270 * @param value {@code true} to flag the specification as a singleton;
271 * {@code false} to not flag the specification as a singleton.
272 *
273 * @see PropertyOverwriteConstraintException
274 * @deprecated Replaced by {@link #setScope(int) setScope(value ? SCOPE_SINGLETON : SCOPE_MULTITON)}.
275 */
276 public void setSingleton( boolean value )
277 {
278 this.scope = value ? SCOPE_SINGLETON : SCOPE_MULTITON;
279 }
280
281 /**
282 * Gets the scope the specification applies to.
283 *
284 * @return scope the specification applies to.
285 *
286 * @see #SCOPE_MULTITON
287 * @see #SCOPE_CONTEXT
288 * @see #SCOPE_SINGLETON
289 * @see PropertyOverwriteConstraintException
290 */
291 public int getScope()
292 {
293 return this.scope;
294 }
295
296 /**
297 * Setter for property {@code scope}.
298 *
299 * @param value new scope the specification applies to.
300 *
301 * @throws IllegalArgumentException if {@code value} is not equal to one of
302 * the constants {@code SCOPE_MULTITON}, {@code SCOPE_CONTEXT} or
303 * {@code SCOPE_SINGLETON}.
304 *
305 * @see #SCOPE_MULTITON
306 * @see #SCOPE_CONTEXT
307 * @see #SCOPE_SINGLETON
308 * @see PropertyOverwriteConstraintException
309 */
310 public void setScope( final int value )
311 {
312 if ( value != SCOPE_MULTITON && value != SCOPE_CONTEXT &&
313 value != SCOPE_SINGLETON )
314 {
315 throw new IllegalArgumentException( Integer.toString( value ) );
316 }
317
318 this.scope = value;
319 }
320
321 /**
322 * Gets the flag indicating if state need not be retained across method
323 * invocations for implementations to operate as specified.
324 *
325 * @return {@code true} if state need not be retained across method
326 * invocations for implementations to operate as specified; {@code false} if
327 * state must be retained across method invocations for implementations
328 * to operate as specified.
329 */
330 public boolean isStateless()
331 {
332 return this.stateless;
333 }
334
335 /**
336 * Setter for property {@code stateless}.
337 *
338 * @param value {@code true} if state need not be retained across method
339 * invocations for implementations to operate as specified; {@code false} if
340 * state must be retained across method invocations for implementations to
341 * operate as specified.
342 */
343 public void setStateless( boolean value )
344 {
345 this.stateless = value;
346 }
347
348 /**
349 * Gets the implementation multiplicity of the specification.
350 *
351 * @return one of the constants {@code MULTIPLICITY_ONE} or
352 * {@code MULTIPLICITY_MANY}.
353 *
354 * @see #MULTIPLICITY_ONE
355 * @see #MULTIPLICITY_MANY
356 * @see MultiplicityConstraintException
357 */
358 public int getMultiplicity()
359 {
360 return this.multiplicity;
361 }
362
363 /**
364 * Setter for property {@code multiplicity}.
365 *
366 * @param value the new implementation multiplicity of the specification.
367 *
368 * @throws IllegalArgumentException if {@code value} is not equal to one of
369 * the constants {@code MULTIPLICITY_ONE} or {@code MULTIPLICITY_MANY}.
370 * @throws MultiplicityConstraintException if {@code value} equals
371 * {@code MULTIPLICITY_ONE} and the specification currently has more than
372 * one implementation defined.
373 *
374 * @see #MULTIPLICITY_ONE
375 * @see #MULTIPLICITY_MANY
376 */
377 public void setMultiplicity( final int value )
378 {
379 if ( value != MULTIPLICITY_ONE && value != MULTIPLICITY_MANY )
380 {
381 throw new IllegalArgumentException( Integer.toString( value ) );
382 }
383 if ( value == MULTIPLICITY_ONE && this.getImplementations().size() > 1 )
384 {
385 throw new MultiplicityConstraintException( this.getIdentifier() );
386 }
387
388 this.multiplicity = value;
389 }
390
391 /**
392 * Gets the vendor of the specification.
393 *
394 * @return the vendor of the specification.
395 */
396 public String getVendor()
397 {
398 if ( this.vendor == null )
399 {
400 this.vendor = "";
401 }
402
403 return this.vendor;
404 }
405
406 /**
407 * Setter for property {@code vendor}.
408 *
409 * @param value the new vendor of the specification.
410 */
411 public void setVendor( final String value )
412 {
413 this.vendor = value;
414 }
415
416 /**
417 * Gets the version of the specification.
418 *
419 * @return the version of the specification or {@code null}.
420 */
421 public String getVersion()
422 {
423 return this.version;
424 }
425
426 /**
427 * Setter for property {@code version}.
428 *
429 * @param value the new version of the specification.
430 */
431 public void setVersion( final String value )
432 {
433 this.version = value;
434 }
435
436 /**
437 * Gets an implementation for a name.
438 *
439 * @param name the name of the implementation to return.
440 *
441 * @return a reference to the implementation named {@code name}.
442 *
443 * @throws NullPointerException if {@code name} is {@code null}.
444 * @throws MissingImplementationException if no implementation matching
445 * {@code name} exists.
446 */
447 public Implementation getImplementation( final String name )
448 {
449 if ( name == null )
450 {
451 throw new NullPointerException( "name" );
452 }
453
454 final Implementation ret =
455 (Implementation) this.implementationNames.get( name );
456
457 if ( ret == null )
458 {
459 throw new MissingImplementationException( name );
460 }
461
462 return ret;
463 }
464
465 /**
466 * Gets all available implementations of the specification.
467 *
468 * @return all available implementations of the specification.
469 */
470 public Implementations getImplementations()
471 {
472 if ( this.implementations == null )
473 {
474 this.implementations = new Implementations();
475 }
476
477 return this.implementations;
478 }
479
480 /**
481 * Setter for property {@code implementations}.
482 *
483 * @param value the new implementations of the specification.
484 *
485 * @throws DuplicateImplementationException if {@code value} contains
486 * duplicate implementations.
487 * @throws MultiplicityConstraintException if the specification's
488 * multiplicity equals {@code MULTIPLICITY_ONE} and {@code value} contains
489 * no or more than one implementation.
490 */
491 public void setImplementations( final Implementations value )
492 {
493 if ( this.getMultiplicity() == MULTIPLICITY_ONE && value != null &&
494 value.size() != 1 )
495 {
496 throw new MultiplicityConstraintException( this.getIdentifier() );
497 }
498
499 this.implementationNames.clear();
500 this.implementations = null;
501
502 if ( value != null )
503 {
504 for ( int i = value.size() - 1; i >= 0; i-- )
505 {
506 if ( this.implementationNames.put(
507 value.getImplementation( i ).getName(),
508 value.getImplementation( i ) ) != null )
509 {
510 this.implementationNames.clear();
511
512 throw new DuplicateImplementationException(
513 value.getImplementation( i ).getName() );
514
515 }
516 }
517
518 this.implementations = value;
519 }
520 }
521
522 /**
523 * Gets the properties of the specification.
524 *
525 * @return the properties of the specification.
526 */
527 public Properties getProperties()
528 {
529 if ( this.properties == null )
530 {
531 this.properties = new Properties();
532 }
533
534 return this.properties;
535 }
536
537 /**
538 * Setter for property {@code properties}.
539 *
540 * @param value new properties of the specification.
541 */
542 public void setProperties( final Properties value )
543 {
544 this.properties = value;
545 }
546
547 /**
548 * Creates a string representing the properties of the instance.
549 *
550 * @return a string representing the properties of the instance.
551 */
552 private String internalString()
553 {
554 final StringBuffer buf = new StringBuffer( 500 ).append( '{' ).
555 append( this.internalString( this ) ).
556 append( ", identifier=" ).append( this.identifier ).
557 append( ", moduleName=" ).append( this.moduleName ).
558 append( ", stateless=" ).append( this.stateless ).
559 append( ", multiplicity=" ).
560 append( this.multiplicity == MULTIPLICITY_ONE ? "one" : "many" ).
561 append( ", scope=" ).append( this.scope == SCOPE_MULTITON
562 ? "multiton"
563 : this.scope == SCOPE_CONTEXT
564 ? "context"
565 : "singleton" ).
566 append( ", vendor=" ).append( this.vendor ).
567 append( ", version=" ).append( this.version ).
568 append( ", properties=" ).append( this.getProperties() ).
569 append( ", implementations={" );
570
571 for ( int i = this.getImplementations().size() - 1; i >= 0; i-- )
572 {
573 final Implementation impl =
574 this.getImplementations().getImplementation( i );
575
576 buf.append( "[" ).append( i ).append( "]=" ).
577 append( impl.getIdentifier() ).append( "@" ).
578 append( impl.getVersion() );
579
580 if ( i - 1 >= 0 )
581 {
582 buf.append( ", " );
583 }
584 }
585
586 buf.append( "}}" );
587 return buf.toString();
588 }
589
590 //-----------------------------------------------------------Specification--
591 //--Serializable------------------------------------------------------------
592
593 /**
594 * Takes care of initializing fields when constructed from an 1.0.x object
595 * stream.
596 *
597 * @throws ObjectStreamException if no scope can be resolved.
598 */
599 private Object readResolve() throws ObjectStreamException
600 {
601 if ( this.scope == SCOPE_MULTITON && this.singleton )
602 {
603 this.scope = SCOPE_SINGLETON;
604 }
605 if ( this.getDocumentation().getValue() == null &&
606 this.description != null )
607 {
608 this.getDocumentation().setValue( this.description );
609 }
610
611 return this;
612 }
613
614 //------------------------------------------------------------Serializable--
615 //--Object------------------------------------------------------------------
616
617 /**
618 * Returns a string representation of the object.
619 *
620 * @return a string representation of the object.
621 */
622 public String toString()
623 {
624 return super.toString() + this.internalString();
625 }
626
627 /**
628 * Creates and returns a copy of this object. This method performs a
629 * "shallow copy" of this object, not a "deep copy" operation.
630 *
631 * @return a clone of this instance.
632 */
633 public Object clone()
634 {
635 try
636 {
637 return super.clone();
638 }
639 catch ( CloneNotSupportedException e )
640 {
641 throw new AssertionError( e );
642 }
643 }
644
645 /**
646 * Indicates whether some other object is equal to this one by comparing
647 * properties {@code identifier} and {@code version}.
648 *
649 * @param o the reference object with which to compare.
650 *
651 * @return {@code true} if this object is the same as {@code o};
652 * {@code false} otherwise.
653 */
654 public final boolean equals( final Object o )
655 {
656 boolean equal = o == this;
657 if ( !equal && o instanceof Specification )
658 {
659 final Specification that = (Specification) o;
660 equal = this.getIdentifier().equals( that.getIdentifier() ) &&
661 ( this.getVersion() == null ? that.getVersion() == null
662 : this.getVersion().equals( that.getVersion() ) );
663
664 }
665
666 return equal;
667 }
668
669 /**
670 * Returns a hash code value for this object.
671 *
672 * @return a hash code value for this object.
673 */
674 public final int hashCode()
675 {
676 return this.getIdentifier().hashCode() +
677 ( this.getVersion() == null ? 0 : this.getVersion().hashCode() );
678
679 }
680
681 //------------------------------------------------------------------Object--
682 }