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    }