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