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 }