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.ObjectStreamException;
024import java.io.Serializable;
025import java.util.Map;
026import java.util.TreeMap;
027
028/**
029 * Specification meta-data.
030 * <p>A specification consists of the properties {@code identifier},
031 * {@code vendor}, {@code description} and {@code version}. Property
032 * {@code identifier} holds an identifier uniquely identifying the specification
033 * in a set of specifications. Property {@code vendor} holds vendor information
034 * for the vendor providing the specification. Property {@code description}
035 * holds a textual description and property {@code version} holds a textual
036 * version of the specification. The {@code stateless} flag indicates that state
037 * does not need to be retained across operations for instances to operate as
038 * specified. Property {@code multiplicity} specifies the number of
039 * implementations allowed to exist among a set of modules. A specification with
040 * {@code MULTIPLICITY_ONE} specifies that exactly one implementation of the
041 * specification must exist among a set of modules. A specification with
042 * {@code MULTIPLICITY_MANY} specifies that multiple implementations of the
043 * specification are allowed to exist among a set of modules (including none).
044 * Property {@code scope} specifies the scope the specification applies to.
045 * In multiton scope, a new instance is created whenever requested. In context
046 * scope, instances are bound to a system's context. An instance is only created
047 * if not already available in context. In singleton scope, instances are bound
048 * to a system's single instance store. An instance is only created if not
049 * already available in that single instance store.</p>
050 *
051 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
052 * @version $JDTAUS: Specification.java 8743 2012-10-07 03:06:20Z schulte $
053 */
054public class Specification extends ModelObject
055    implements Cloneable, Serializable
056{
057    //--Constants---------------------------------------------------------------
058
059    /**
060     * Constant for property {@code multiplicity}.
061     * <p>A specification with {@code MULTIPLICITY_ONE} specifies that exactly
062     * one implementation of the specification must exist among a set of
063     * modules.</p>
064     */
065    public static final int MULTIPLICITY_ONE = 25000;
066
067    /**
068     * Constant for property {@code multiplicity}.
069     * <p>A specification with {@code MULTIPLICITY_MANY} specifies that multiple
070     * implementations of the specification are allowed to exist among a set of
071     * modules (including none).</p>
072     */
073    public static final int MULTIPLICITY_MANY = 25001;
074
075    /**
076     * Constant for property {@code scope}.
077     * <p>In multiton scope, a new instance is created whenever requested.</p>
078     */
079    public static final int SCOPE_MULTITON = 26000;
080
081    /**
082     * Constant for property {@code scope}.
083     * <p>In context scope, instances are bound to a system's context. An
084     * instance is only created if not already available in context.</p>
085     */
086    public static final int SCOPE_CONTEXT = 26001;
087
088    /**
089     * Constant for property {@code scope}.
090     * <p>In singleton scope, instances are bound to a system's single instance
091     * store. An instance is only created if not already available in that
092     * single instance store.</p>
093     */
094    public static final int SCOPE_SINGLETON = 26002;
095
096    /** Serial version UID for backwards compatibility with 1.0.x classes. */
097    private static final long serialVersionUID = -1829249262406961967L;
098
099    //---------------------------------------------------------------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}