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