View Javadoc

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 }