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