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 }