View Javadoc

1   /*
2    *  jDTAUS Core API
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.container;
22  
23  import java.io.Serializable;
24  import java.util.Comparator;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.TreeMap;
28  import java.util.TreeSet;
29  
30  /**
31   * Implementation meta-data.
32   * <p>An implementation consists of the properties {@code identifier},
33   * {@code name}, {@code description}, {@code vendor} and {@code version}.
34   * Property {@code identifier} holds an identifier uniquely identifying the
35   * implementation in a collection of implementations. Property {@code name}
36   * holds a name of the implementation uniquely identifying the implementation
37   * for a specification. Property {@code description} holds a textual
38   * description. Property {@code vendor} holds vendor information for the vendor
39   * providing the implementation. Property {@code version} holds a textual
40   * version of the implementation. Properties, dependencies and implemented
41   * specifications may be inherited from a {@code parent} up the hierarchy.
42   * Property {@code final} flags an implementation as the final node in an
43   * inheritance hierarchy.</p>
44   *
45   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
46   * @version $JDTAUS: Implementation.java 8743 2012-10-07 03:06:20Z schulte $
47   */
48  public class Implementation extends ModelObject
49      implements Cloneable, Serializable
50  {
51      //--Constants---------------------------------------------------------------
52  
53      /** Serial version UID for backwards compatibility with 1.0.x classes. */
54      private static final long serialVersionUID = -7590949013151607828L;
55  
56      //---------------------------------------------------------------Constants--
57      //--Implementation----------------------------------------------------------
58  
59      /**
60       * The parent to inherit from.
61       * @serial
62       */
63      private Implementation parent;
64  
65      /**
66       * The name of the module holding the implementation.
67       * @serial
68       */
69      private String moduleName;
70  
71      /**
72       * The specifications the implementation implements.
73       * @serial
74       */
75      private Specifications implementedSpecifications;
76  
77      /**
78       * The dependencies of the implementation.
79       * @serial
80       */
81      private Dependencies dependencies;
82  
83      /**
84       * The properties of the implementation.
85       * @serial
86       */
87      private Properties properties;
88  
89      /**
90       * The messages of the implementation.
91       * @serial
92       */
93      private Messages messages;
94  
95      /**
96       * The identifier of the implementation.
97       * @serial
98       */
99      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 }