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}