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.Serializable;
026    import java.util.Comparator;
027    import java.util.Map;
028    import java.util.Set;
029    import java.util.TreeMap;
030    import java.util.TreeSet;
031    
032    /**
033     * Implementation meta-data.
034     * <p>An implementation consists of the properties {@code identifier},
035     * {@code name}, {@code description}, {@code vendor} and {@code version}.
036     * Property {@code identifier} holds an identifier uniquely identifying the
037     * implementation in a collection of implementations. Property {@code name}
038     * holds a name of the implementation uniquely identifying the implementation
039     * for a specification. Property {@code description} holds a textual
040     * description. Property {@code vendor} holds vendor information for the vendor
041     * providing the implementation. Property {@code version} holds a textual
042     * version of the implementation. Properties, dependencies and implemented
043     * specifications may be inherited from a {@code parent} up the hierarchy.
044     * Property {@code final} flags an implementation as the final node in an
045     * inheritance hierarchy.</p>
046     *
047     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
048     * @version $Id: Implementation.java 8044 2009-07-02 01:29:05Z schulte2005 $
049     */
050    public class Implementation extends ModelObject
051        implements Cloneable, Serializable
052    {
053        //--Constants---------------------------------------------------------------
054    
055        /** Serial version UID for backwards compatibility with 1.0.x classes. */
056        private static final long serialVersionUID = -7590949013151607828L;
057    
058        //---------------------------------------------------------------Constants--
059        //--Implementation----------------------------------------------------------
060    
061        /**
062         * The parent to inherit from.
063         * @serial
064         */
065        private Implementation parent;
066    
067        /**
068         * The name of the module holding the implementation.
069         * @serial
070         */
071        private String moduleName;
072    
073        /**
074         * The specifications the implementation implements.
075         * @serial
076         */
077        private Specifications implementedSpecifications;
078    
079        /**
080         * The dependencies of the implementation.
081         * @serial
082         */
083        private Dependencies dependencies;
084    
085        /**
086         * The properties of the implementation.
087         * @serial
088         */
089        private Properties properties;
090    
091        /**
092         * The messages of the implementation.
093         * @serial
094         */
095        private Messages messages;
096    
097        /**
098         * The identifier of the implementation.
099         * @serial
100         */
101        private String identifier;
102    
103        /**
104         * The name of the implementation.
105         * @serial
106         */
107        private String name;
108    
109        /**
110         * The vendor of the implementation.
111         * @serial
112         */
113        private String vendor;
114    
115        /**
116         * The version of the implementation.
117         * @serial
118         */
119        private String version;
120    
121        /**
122         * Flag indicating if the implementation is a final node in an inheritance
123         * hierarchy.
124         * @serial
125         */
126        private boolean finalFlag;
127    
128        /**
129         * Gets the name of the module holding the implementation.
130         *
131         * @return the name of the module holding the implementation.
132         */
133        public String getModuleName()
134        {
135            if ( this.moduleName == null )
136            {
137                this.moduleName = "";
138            }
139    
140            return this.moduleName;
141        }
142    
143        /**
144         * Setter for property {@code moduleName}.
145         *
146         * @param value the new name of the module holding the implementation.
147         */
148        public void setModuleName( final String value )
149        {
150            this.moduleName = value;
151        }
152    
153        /**
154         * Gets the specifications the implementation implements.
155         * <p>The specifications an implementation implements are a merged set of
156         * all specifications implemented by any parent overwritten by any declared
157         * implemented specifications.</p>
158         *
159         * @return the specifications the implementation implements.
160         */
161        public Specifications getImplementedSpecifications()
162        {
163            Specifications specs = this.getDeclaredImplementedSpecifications();
164    
165            if ( this.getParent() != null )
166            {
167                final TreeMap map = new TreeMap();
168                for ( int i = specs.size() - 1; i >= 0; i-- )
169                {
170                    final Specification declaredSpecification =
171                        specs.getSpecification( i );
172    
173                    map.put( declaredSpecification.getIdentifier(),
174                             declaredSpecification );
175    
176                }
177    
178                final Specifications superSpecifications =
179                    this.getParent().getImplementedSpecifications();
180    
181                for ( int i = superSpecifications.size() - 1; i >= 0; i-- )
182                {
183                    final Specification superSpecification =
184                        superSpecifications.getSpecification( i );
185    
186                    if ( !map.containsKey( superSpecification.getIdentifier() ) )
187                    {
188                        map.put( superSpecification.getIdentifier(),
189                                 superSpecification );
190    
191                    }
192                }
193    
194                specs = new Specifications();
195                specs.setSpecifications( (Specification[]) map.values().toArray(
196                                         new Specification[ map.size() ] ) );
197    
198            }
199    
200            return specs;
201        }
202    
203        /**
204         * Setter for property {@code implementedSpecifications}.
205         *
206         * @param value the new specifications the implementation implements.
207         */
208        public void setImplementedSpecifications( final Specifications value )
209        {
210            this.implementedSpecifications = value;
211        }
212    
213        /**
214         * Gets the specifications the implementation declares to implement.
215         *
216         * @return the specifications the implementation declares to implement.
217         */
218        public Specifications getDeclaredImplementedSpecifications()
219        {
220            if ( this.implementedSpecifications == null )
221            {
222                this.implementedSpecifications = new Specifications();
223            }
224    
225            return this.implementedSpecifications;
226        }
227    
228        /**
229         * Gets the dependencies of the implementation.
230         * <p>The dependencies of an implementation are a merged set of all parent
231         * dependencies overwritten by any declared dependencies.</p>
232         *
233         * @return the dependencies the implementation depends on.
234         *
235         * @throws IllegalDependencyTypeException for any dependency type collisions
236         * during merging.
237         */
238        public Dependencies getDependencies()
239        {
240            Dependencies deps = this.getDeclaredDependencies();
241    
242            if ( this.getParent() != null )
243            {
244                final Map map = new TreeMap();
245                for ( int i = deps.size() - 1; i >= 0; i-- )
246                {
247                    final Dependency declaredDependency = deps.getDependency( i );
248                    map.put( declaredDependency.getName(), declaredDependency );
249                }
250    
251                final Dependencies superDependencies =
252                    this.getParent().getDependencies();
253    
254                for ( int i = superDependencies.size() - 1; i >= 0; i-- )
255                {
256                    final Dependency superDependency =
257                        superDependencies.getDependency( i );
258    
259                    final Dependency declaredDependency =
260                        (Dependency) map.get( superDependency.getName() );
261    
262                    if ( declaredDependency != null )
263                    {
264                        if ( !declaredDependency.getSpecification().getIdentifier().
265                            equals( superDependency.getSpecification().
266                                    getIdentifier() ) )
267                        {
268                            throw new IllegalDependencyTypeException(
269                                this.getIdentifier(),
270                                declaredDependency.getName(),
271                                declaredDependency.getSpecification().
272                                getIdentifier(),
273                                superDependency.getSpecification().
274                                getIdentifier() );
275    
276                        }
277                    }
278                    else
279                    {
280                        map.put( superDependency.getName(), superDependency );
281                    }
282                }
283    
284                deps = new Dependencies();
285                deps.setDependencies( (Dependency[]) map.values().toArray(
286                                      new Dependency[ map.size() ] ) );
287    
288            }
289    
290            return deps;
291        }
292    
293        /**
294         * Setter for property {@code dependencies}.
295         *
296         * @param value the new dependencies of the implementation.
297         */
298        public void setDependencies( final Dependencies value )
299        {
300            this.dependencies = value;
301        }
302    
303        /**
304         * Gets the declared dependencies of the implementation.
305         *
306         * @return the declared dependencies of the implementation.
307         */
308        public Dependencies getDeclaredDependencies()
309        {
310            if ( this.dependencies == null )
311            {
312                this.dependencies = new Dependencies();
313            }
314    
315            return this.dependencies;
316        }
317    
318        /**
319         * Gets the properties of the implementation.
320         * <p>The properties of an implementation are a merged set of all parent
321         * properties overwritten by any declared properties.</p>
322         *
323         * @return the properties of the implementation.
324         *
325         * @throws IllegalPropertyTypeException for any property type collisions
326         * during merging.
327         * @throws PropertyOverwriteConstraintException if the implementation does
328         * not provide values for all properties declared for its implemented
329         * specifications.
330         */
331        public Properties getProperties()
332        {
333            final Map declaredProperties = new TreeMap();
334            final Map implementedProperties = new TreeMap();
335    
336            for ( int i = this.getDeclaredProperties().size() - 1; i >= 0; i-- )
337            {
338                final Property declaredProperty =
339                    this.getDeclaredProperties().getProperty( i );
340    
341                declaredProperties.put( declaredProperty.getName(),
342                                        declaredProperty );
343    
344            }
345    
346            if ( this.getParent() != null )
347            {
348    
349                final Properties superProperties =
350                    this.getParent().getProperties();
351    
352                for ( int i = superProperties.size() - 1; i >= 0; i-- )
353                {
354                    final Property superProperty =
355                        superProperties.getProperty( i );
356    
357                    final Property declaredProperty =
358                        (Property) declaredProperties.get(
359                        superProperty.getName() );
360    
361                    if ( declaredProperty != null )
362                    {
363                        if ( !declaredProperty.getType().equals(
364                            superProperty.getType() ) )
365                        {
366                            throw new IllegalPropertyTypeException(
367                                declaredProperty.getName(),
368                                declaredProperty.getType(),
369                                superProperty.getType() );
370    
371                        }
372    
373                        if ( declaredProperty.getDocumentation().
374                            getValue() == null &&
375                            superProperty.getDocumentation().
376                            getValue() != null )
377                        {
378                            declaredProperty.setDocumentation(
379                                superProperty.getDocumentation() );
380    
381                        }
382                    }
383                    else
384                    {
385                        declaredProperties.put( superProperty.getName(),
386                                                superProperty );
387    
388                    }
389                }
390            }
391    
392            final Specifications specs = this.getImplementedSpecifications();
393    
394            for ( int i = specs.size() - 1; i >= 0; i-- )
395            {
396                final Specification spec = specs.getSpecification( i );
397                for ( int p = spec.getProperties().size() - 1; p >= 0; p-- )
398                {
399                    final Property implementedProperty =
400                        spec.getProperties().getProperty( p );
401    
402                    final Property alreadyImplemented =
403                        (Property) implementedProperties.put(
404                        implementedProperty.getName(),
405                        implementedProperty );
406    
407                    if ( alreadyImplemented != null &&
408                        !alreadyImplemented.getType().equals(
409                        implementedProperty.getType() ) )
410                    {
411                        throw new IllegalPropertyTypeException(
412                            implementedProperty.getName(),
413                            implementedProperty.getType(),
414                            alreadyImplemented.getType() );
415    
416                    }
417    
418                    final Property declaredProperty =
419                        (Property) declaredProperties.get(
420                        implementedProperty.getName() );
421    
422                    if ( declaredProperty != null )
423                    {
424                        if ( !declaredProperty.getType().equals(
425                            implementedProperty.getType() ) )
426                        {
427                            throw new IllegalPropertyTypeException(
428                                declaredProperty.getName(),
429                                declaredProperty.getType(),
430                                implementedProperty.getType() );
431    
432                        }
433    
434                        if ( declaredProperty.getDocumentation().
435                            getValue() == null &&
436                            implementedProperty.getDocumentation().
437                            getValue() != null )
438                        {
439                            declaredProperty.setDocumentation(
440                                implementedProperty.getDocumentation() );
441    
442                        }
443    
444                        declaredProperty.setApi( true );
445                    }
446                    else
447                    {
448                        throw new PropertyOverwriteConstraintException(
449                            this.getIdentifier(), spec.getIdentifier(),
450                            implementedProperty.getName() );
451    
452                    }
453                }
454            }
455    
456            final Properties p = new Properties();
457            p.setProperties( (Property[]) declaredProperties.values().toArray(
458                             new Property[ declaredProperties.size() ] ) );
459    
460            return p;
461        }
462    
463        /**
464         * Setter for property {@code properties}.
465         *
466         * @param value new properties of the implementation.
467         */
468        public void setProperties( final Properties value )
469        {
470            this.properties = value;
471        }
472    
473        /**
474         * Gets the declared properties of the implementation.
475         *
476         * @return the declared properties of the implementation.
477         */
478        public Properties getDeclaredProperties()
479        {
480            if ( this.properties == null )
481            {
482                this.properties = new Properties();
483            }
484    
485            return this.properties;
486        }
487    
488        /**
489         * Gets the messages of the implementation.
490         * <p>The messages of an implementation are a merged set of all parent
491         * messages overwritten by any declared messages.</p>
492         *
493         * @return the messages of the implementation.
494         */
495        public Messages getMessages()
496        {
497            Messages msgs = this.getDeclaredMessages();
498    
499            if ( this.getParent() != null )
500            {
501                final Set set = new TreeSet( new Comparator()
502                {
503    
504                    public int compare( final Object o1, final Object o2 )
505                    {
506                        return ( (Message) o1 ).getName().
507                            compareTo( ( (Message) o2 ).getName() );
508    
509                    }
510    
511                } );
512    
513                for ( int i = msgs.size() - 1; i >= 0; i-- )
514                {
515                    set.add( msgs.getMessage( i ) );
516                }
517    
518                final Messages parentMessages = this.getParent().getMessages();
519                for ( int i = parentMessages.size() - 1; i >= 0; i-- )
520                {
521                    final Message parentMessage = parentMessages.getMessage( i );
522                    if ( !set.contains( parentMessage ) )
523                    {
524                        set.add( parentMessage );
525                    }
526                }
527    
528                msgs = new Messages();
529                msgs.setMessages(
530                    (Message[]) set.toArray( new Message[ set.size() ] ) );
531    
532            }
533    
534            return msgs;
535        }
536    
537        /**
538         * Setter for property {@code messages}.
539         *
540         * @param value new messages of the implementation.
541         */
542        public void setMessages( final Messages value )
543        {
544            this.messages = value;
545        }
546    
547        /**
548         * Gets the declared messages of the implementation.
549         *
550         * @return the declared messages of the implementation.
551         */
552        public Messages getDeclaredMessages()
553        {
554            if ( this.messages == null )
555            {
556                this.messages = new Messages();
557            }
558    
559            return this.messages;
560        }
561    
562        /**
563         * Gets the identifier of the implementation.
564         *
565         * @return the unique identifier of the implementation.
566         */
567        public String getIdentifier()
568        {
569            if ( this.identifier == null )
570            {
571                this.identifier = "";
572            }
573    
574            return this.identifier;
575        }
576    
577        /**
578         * Setter for property {@code identifier}.
579         *
580         * @param value the new identifier of the implementation.
581         */
582        public void setIdentifier( final String value )
583        {
584            this.identifier = value;
585        }
586    
587        /**
588         * Gets the name of the implementation.
589         *
590         * @return the name of the implementation.
591         */
592        public String getName()
593        {
594            if ( this.name == null )
595            {
596                this.name = "";
597            }
598    
599            return name;
600        }
601    
602        /**
603         * Setter for property {@code name}.
604         *
605         * @param value the new name of the implementation.
606         */
607        public void setName( final String value )
608        {
609            this.name = value;
610        }
611    
612        /**
613         * Gets the parent implementation the implementation inherits from.
614         *
615         * @return the parent implementation the implementation inherits from or
616         * {@code null} if the implementation has no parent.
617         *
618         * @see InheritanceConstraintException
619         */
620        public Implementation getParent()
621        {
622            return this.parent;
623        }
624    
625        /**
626         * Setter for property {@code parent}.
627         *
628         * @param value the new parent implementation of the implementation.
629         *
630         * @throws InheritanceConstraintException if {@code value} is flagged as
631         * final.
632         */
633        public void setParent( final Implementation value )
634        {
635            if ( value != null && value.isFinal() )
636            {
637                throw new InheritanceConstraintException( this.getIdentifier() );
638            }
639    
640            this.parent = value;
641        }
642    
643        /**
644         * Gets the vendor of the implementation.
645         *
646         * @return the vendor of the implementation.
647         */
648        public String getVendor()
649        {
650            if ( this.vendor == null )
651            {
652                this.vendor = "";
653            }
654    
655            return this.vendor;
656        }
657    
658        /**
659         * Setter for property {@code name}.
660         *
661         * @param value the new vendor of the implementation.
662         */
663        public void setVendor( final String value )
664        {
665            this.vendor = value;
666        }
667    
668        /**
669         * Gets the version of the implementation.
670         *
671         * @return the version of the implementation or {@code null}.
672         */
673        public String getVersion()
674        {
675            return this.version;
676        }
677    
678        /**
679         * Setter for property {@code version}.
680         *
681         * @param value the new version of the implementation.
682         */
683        public void setVersion( final String value )
684        {
685            this.version = value;
686        }
687    
688        /**
689         * Gets a flag indicating if the implementation is a final node
690         * in an inheritance hierarchy.
691         *
692         * @return {@code true} if the implementation is a final node in an
693         * inheritance hierarchy; {@code false} if the implementation is allowed
694         * to be the parent of another implementation.
695         *
696         * @see InheritanceConstraintException
697         */
698        public boolean isFinal()
699        {
700            return this.finalFlag;
701        }
702    
703        /**
704         * Setter for property {@code final}.
705         *
706         * @param value {@code true} if the implementation is a final node in an
707         * inheritance hierarchy; {@code false} if the implementation is allowed
708         * to be the parent of another implementation.
709         *
710         * @see InheritanceConstraintException
711         */
712        public void setFinal( final boolean value )
713        {
714            this.finalFlag = value;
715        }
716    
717        /**
718         * Creates a string representing the properties of the instance.
719         *
720         * @return a string representing the properties of the instance.
721         */
722        private String internalString()
723        {
724            final StringBuffer buf = new StringBuffer( 500 ).append( '{' ).
725                append( this.internalString( this ) ).
726                append( ", identifier=" ).append( this.identifier ).
727                append( ", moduleName=" ).append( this.moduleName ).
728                append( ", name=" ).append( this.name ).
729                append( ", parent=" ).append( this.parent ).
730                append( ", properties=" ).append( this.properties ).
731                append( ", vendor=" ).append( this.vendor ).
732                append( ", version=" ).append( this.version ).
733                append( ", final=" ).append( this.finalFlag ).
734                append( ", dependencies=" ).append( this.dependencies ).
735                append( ", messages=" ).append( this.messages ).
736                append( ", specifications={" );
737    
738            for ( int i = this.getImplementedSpecifications().size() - 1; i > 0;
739                i-- )
740            {
741                final Specification s =
742                    this.getImplementedSpecifications().getSpecification( i );
743    
744                buf.append( "[" ).append( i ).append( "]=" ).
745                    append( s.getIdentifier() ).append( "@" ).
746                    append( s.getVersion() );
747    
748            }
749    
750            buf.append( "}}" );
751            return buf.toString();
752        }
753    
754        //----------------------------------------------------------Implementation--
755        //--Object------------------------------------------------------------------
756    
757        /**
758         * Returns a string representation of the object.
759         *
760         * @return a string representation of the object.
761         */
762        public String toString()
763        {
764            return super.toString() + this.internalString();
765        }
766    
767        /**
768         * Creates and returns a copy of this object. This method  performs a
769         * "shallow copy" of this object, not a "deep copy" operation.
770         *
771         * @return a clone of this instance.
772         */
773        public Object clone()
774        {
775            try
776            {
777                return super.clone();
778            }
779            catch ( CloneNotSupportedException e )
780            {
781                throw new AssertionError( e );
782            }
783        }
784    
785        /**
786         * Indicates whether some other object is equal to this one by comparing
787         * properties {@code identifier} and {@code version}.
788         *
789         * @param o the reference object with which to compare.
790         *
791         * @return {@code true} if this object is the same as {@code o};
792         * {@code false} otherwise.
793         */
794        public final boolean equals( final Object o )
795        {
796            boolean equal = this == o;
797    
798            if ( !equal && o instanceof Implementation )
799            {
800                final Implementation that = (Implementation) o;
801                equal = this.getIdentifier().equals( that.getIdentifier() ) &&
802                    ( this.getVersion() == null ? that.getVersion() == null
803                    : this.getVersion().equals( that.getVersion() ) );
804    
805            }
806    
807            return equal;
808        }
809    
810        /**
811         * Returns a hash code value for this object.
812         *
813         * @return a hash code value for this object.
814         */
815        public final int hashCode()
816        {
817            return this.getIdentifier().hashCode() +
818                ( this.getVersion() == null ? 0 : this.getVersion().hashCode() );
819    
820        }
821    
822        //------------------------------------------------------------------Object--
823    }