001 /* 002 * Copyright 2005 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.library.impl; 020 021 import java.io.File; 022 import java.io.IOException; 023 import java.net.URI; 024 import java.net.URISyntaxException; 025 import java.text.SimpleDateFormat; 026 import java.util.Arrays; 027 import java.util.ArrayList; 028 import java.util.List; 029 import java.util.Date; 030 import java.util.TimeZone; 031 import java.util.Properties; 032 import java.util.Map; 033 import java.util.Hashtable; 034 035 import net.dpml.lang.Category; 036 import net.dpml.lang.Version; 037 038 import net.dpml.library.Info; 039 import net.dpml.library.Filter; 040 import net.dpml.library.Library; 041 import net.dpml.library.Module; 042 import net.dpml.library.Resource; 043 import net.dpml.library.Type; 044 import net.dpml.library.Data; 045 import net.dpml.library.ResourceNotFoundException; 046 import net.dpml.library.info.InfoDirective; 047 import net.dpml.library.info.TypeDirective; 048 import net.dpml.library.info.ResourceDirective; 049 import net.dpml.library.info.ResourceDirective.Classifier; 050 import net.dpml.library.info.IncludeDirective; 051 import net.dpml.library.info.DependencyDirective; 052 import net.dpml.library.info.AbstractDirective; 053 import net.dpml.library.info.ValidationException; 054 import net.dpml.library.info.FilterDirective; 055 import net.dpml.library.info.Scope; 056 057 import net.dpml.transit.Artifact; 058 import net.dpml.transit.Transit; 059 060 import net.dpml.util.Resolver; 061 062 063 /** 064 * Implementation of a resource. 065 * 066 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 067 * @version 1.1.2 068 */ 069 public class DefaultResource extends DefaultDictionary implements Resource, Resolver, Comparable 070 { 071 private static final String LEGACY_DECIMAL_PREFIX_KEY = 072 "project.version-prefix.enabled"; 073 074 /** 075 * Timestamp. 076 */ 077 public static final String TIMESTAMP = getTimestamp(); 078 079 /** 080 * Constant SNAPSHOT symbol. 081 */ 082 public static final String SNAPSHOT = "SNAPSHOT"; 083 084 /** 085 * Constant BOOTSTRAP symbol. 086 */ 087 public static final String BOOTSTRAP = "BOOTSTRAP"; 088 089 /** 090 * Constant RELEASE symbol. 091 */ 092 public static final String RELEASE = "RELEASE"; 093 094 /** 095 * Constant ANONYMOUS version symbol. 096 */ 097 public static final String ANONYMOUS = "ANONYMOUS"; 098 099 private final DefaultLibrary m_library; 100 private final ResourceDirective m_directive; 101 private final DefaultModule m_parent; 102 private final Type[] m_types; 103 private final String[] m_typeNames; 104 private final String m_path; 105 private final File m_basedir; 106 private final Map m_filters = new Hashtable(); 107 private final Data[] m_data; 108 109 /** 110 * Creation of a new default resource. 111 * @param logger the assigned logging channel 112 * @param library the reference library 113 * @param directive the directive 114 */ 115 DefaultResource( final DefaultLibrary library, final AbstractDirective directive ) 116 { 117 super( null, directive ); 118 119 m_library = library; 120 m_directive = null; 121 m_parent = null; 122 m_types = new Type[0]; 123 m_typeNames = new String[0]; 124 m_path = ""; 125 m_basedir = null; 126 m_data = new Data[0]; 127 } 128 129 /** 130 * Creation of a new default resource. 131 * @param logger the assigned logging channel 132 * @param library the reference library 133 * @param module the parent module 134 * @param directive the resource directive 135 */ 136 DefaultResource( 137 final DefaultLibrary library, final DefaultModule module, final ResourceDirective directive ) 138 { 139 super( module, directive ); 140 if( null == directive ) 141 { 142 throw new NullPointerException( "directive" ); 143 } 144 145 m_library = library; 146 m_directive = directive; 147 m_parent = module; 148 149 if( module.isRoot() ) 150 { 151 m_path = directive.getName(); 152 } 153 else 154 { 155 m_path = module.getResourcePath() + "/" + directive.getName(); 156 } 157 158 // setup produced types 159 160 m_types = directive.getTypeDirectives(); 161 m_typeNames = new String[ m_types.length ]; 162 for( int i=0; i<m_types.length; i++ ) 163 { 164 Type type = m_types[i]; 165 m_typeNames[i] = type.getID(); 166 } 167 168 // setup production data 169 170 m_data = new Data[0]; 171 172 // setup the resource basedir 173 174 File anchor = getAnchor(); 175 String filename = m_directive.getBasedir(); 176 if( null != filename ) 177 { 178 String spec = resolve( filename ); 179 File file = new File( spec ); 180 if( file.isAbsolute() ) 181 { 182 m_basedir = getCanonicalFile( file ); 183 } 184 else 185 { 186 File basedir = new File( anchor, spec ); 187 m_basedir = getCanonicalFile( basedir ); 188 setProperty( "basedir", m_basedir.toString() ); 189 } 190 } 191 else 192 { 193 if( !m_directive.getClassifier().equals( Classifier.LOCAL ) ) 194 { 195 m_basedir = null; 196 } 197 else 198 { 199 final String error = 200 "Missing base directory declaration in resource [" 201 + m_path 202 + "]."; 203 throw new ValidationException( error ); 204 } 205 } 206 207 // setup the default properties 208 209 setProperty( "project.name", getName() ); 210 if( null != m_parent ) 211 { 212 setProperty( "project.group", m_parent.getResourcePath() ); 213 } 214 else 215 { 216 setProperty( "project.group", "" ); 217 } 218 String version = getVersion(); 219 if( null != version ) 220 { 221 setProperty( "project.version", version ); 222 } 223 224 // setup filters 225 226 FilterDirective[] filters = directive.getFilterDirectives(); 227 for( int i=0; i<filters.length; i++ ) 228 { 229 FilterDirective filter = filters[i]; 230 String token = filter.getToken(); 231 m_filters.put( token, filter ); 232 } 233 } 234 235 //---------------------------------------------------------------------------- 236 // Resource 237 //---------------------------------------------------------------------------- 238 239 /** 240 * Return a data directives. 241 * @return the associated production data 242 */ 243 public Data[] getData() 244 { 245 return m_data; 246 } 247 248 /** 249 * Return the singleton library. 250 * @return the library 251 */ 252 public Library getLibrary() 253 { 254 return m_library; 255 } 256 257 /** 258 * Return the name of the resource. 259 * @return the resource name 260 */ 261 public String getName() 262 { 263 if( null != m_directive ) 264 { 265 return m_directive.getName(); 266 } 267 else 268 { 269 return null; 270 } 271 } 272 273 /** 274 * Return the resource version. 275 * @return the version 276 */ 277 public String getVersion() 278 { 279 String version = getStatutoryVersion(); 280 if( null != version ) 281 { 282 return version; 283 } 284 else 285 { 286 return getStandardVersion(); 287 } 288 } 289 290 /** 291 * Return the declard resource version. 292 * @return the statutory version 293 */ 294 public String getStatutoryVersion() 295 { 296 if( null == m_directive ) 297 { 298 return null; 299 } 300 else 301 { 302 String version = m_directive.getVersion(); 303 if( null != version ) 304 { 305 return version; 306 } 307 else 308 { 309 if( null != m_parent ) 310 { 311 return m_parent.getStatutoryVersion(); 312 } 313 else if( !m_directive.getClassifier().equals( Classifier.LOCAL ) ) 314 { 315 return ANONYMOUS; 316 } 317 else 318 { 319 return null; 320 } 321 } 322 } 323 } 324 325 /** 326 * Return the decimal version. 327 * 328 * @return the version 329 */ 330 public Version getDecimalVersion() 331 { 332 int major = getMajorVersion(); 333 int minor = getMinorVersion(); 334 int micro = getMicroVersion(); 335 return new Version( major, minor, micro ); 336 } 337 338 /** 339 * Return the fully qualified path to the resource. 340 * @return the path 341 */ 342 public String getResourcePath() 343 { 344 return m_path; 345 } 346 347 /** 348 * Return the basedir for this resource. 349 * @return the base directory (possibly null) 350 */ 351 public File getBaseDir() 352 { 353 return m_basedir; 354 } 355 356 /** 357 * Return the resource classifier. 358 * @return the classifier (LOCAL, EXTERNAL or ANONYMOUS) 359 */ 360 public Classifier getClassifier() 361 { 362 if( null != m_directive ) 363 { 364 return m_directive.getClassifier(); 365 } 366 else 367 { 368 return ResourceDirective.ANONYMOUS; 369 } 370 } 371 372 /** 373 * Return the info block. 374 * @return the info block 375 */ 376 public Info getInfo() 377 { 378 return m_directive.getInfoDirective(); 379 } 380 381 /** 382 * Return the expanded array of types associated with the resource. 383 * The returned array is a function of the types declared by a resource 384 * expanded relative to any types implied by processor dependencies. 385 * @return the type array 386 */ 387 public Type[] getTypes() 388 { 389 return m_types; 390 } 391 392 /** 393 * Test if this resource is associated with a type of the supplied name. 394 * @param type the type id 395 * @return TRUE if this resource produces an artifact of the supplied type 396 */ 397 public boolean isa( final String type ) 398 { 399 for( int i=0; i<m_types.length; i++ ) 400 { 401 Type someType = m_types[i]; 402 String name = someType.getID(); 403 if( name.equals( type ) ) 404 { 405 return true; 406 } 407 } 408 return false; 409 } 410 411 /** 412 * Return a resource type relative to a supplied type id. 413 * @param id the type name to retrieve 414 * @return the type instance 415 * @exception IllegalArgumentException if the id value does not match 416 * a type produced by the resource. 417 */ 418 public Type getType( final String id ) throws IllegalArgumentException 419 { 420 for( int i=0; i<m_types.length; i++ ) 421 { 422 Type type = m_types[i]; 423 if( type.getID().equals( id ) ) 424 { 425 return type; 426 } 427 } 428 final String error = 429 "Type name [" 430 + id 431 + "] not recognized with the scope of resource [" 432 + getResourcePath() 433 + "]."; 434 throw new IllegalArgumentException( error ); 435 } 436 437 /** 438 * Construct an link artifact for the supplied type. 439 * @param id the resource type id 440 * @return the link artifact 441 */ 442 public Artifact getLinkArtifact( final String id ) 443 { 444 if( null == m_directive ) 445 { 446 final String error = 447 "Method not supported on virtual root."; 448 throw new UnsupportedOperationException( error ); 449 } 450 if( null == id ) 451 { 452 throw new NullPointerException( "id" ); 453 } 454 String group = getGroupName(); 455 String name = getName(); 456 Type type = getType( id ); 457 Version version = type.getVersion(); 458 if( null == version ) 459 { 460 final String error = 461 "Resource does not declare production of an alias for the requested type." 462 + "\nResource: " + this 463 + "\nType: " + id; 464 throw new IllegalArgumentException( error ); 465 } 466 try 467 { 468 String spec = "link:" + id; 469 if( null != group ) 470 { 471 spec = spec + ":" + group + "/" + name; 472 } 473 else 474 { 475 spec = spec + ":" + name; 476 } 477 if( !Version.NULL_VERSION.equals( version ) ) 478 { 479 int major = version.getMajor(); 480 int minor = version.getMinor(); 481 spec = spec + "#" 482 + major 483 + "." 484 + minor; 485 } 486 return Artifact.createArtifact( spec ); 487 } 488 catch( Throwable e ) 489 { 490 final String error = 491 "Failed to construct link artifact for resource [" 492 + getResourcePath() 493 + "]."; 494 throw new RuntimeException( error, e ); 495 } 496 } 497 498 /** 499 * Construct an artifact for the supplied type. 500 * @param id the resource type identifier 501 * @return the artifact 502 */ 503 public Artifact getArtifact( final String id ) 504 { 505 if( null == m_directive ) 506 { 507 final String error = 508 "Method not supported on virtual root."; 509 throw new UnsupportedOperationException( error ); 510 } 511 if( null == id ) 512 { 513 throw new NullPointerException( "id" ); 514 } 515 516 String group = getGroupName(); 517 String name = getName(); 518 String version = getVersion(); 519 String scheme = m_directive.getScheme(); 520 521 try 522 { 523 return Artifact.createArtifact( scheme, group, name, version, id ); 524 } 525 catch( Throwable e ) 526 { 527 final String error = 528 "Failed to construct artifact for resource [" 529 + getResourcePath() 530 + "]."; 531 throw new RuntimeException( error, e ); 532 } 533 } 534 535 /** 536 * Return the enclosing parent module. 537 * @return the enclosing module of null if this a top-level module. 538 */ 539 public Module getParent() 540 { 541 return getDefaultParent(); 542 } 543 544 /** 545 * Return an array of filters associated with the resource. 546 * @return the array of filters 547 */ 548 public Filter[] getFilters() 549 { 550 DefaultModule module = getDefaultParent(); 551 if( null != module ) 552 { 553 Map map = new Hashtable(); 554 Filter[] filters = module.getFilters(); 555 for( int i=0; i<filters.length; i++ ) 556 { 557 Filter filter = filters[i]; 558 String token = filter.getToken(); 559 map.put( token, filter ); 560 } 561 Filter[] local = getLocalFilters(); 562 for( int i=0; i<local.length; i++ ) 563 { 564 Filter filter = local[i]; 565 String token = filter.getToken(); 566 map.put( token, filter ); 567 } 568 return (Filter[]) map.values().toArray( new Filter[0] ); 569 } 570 else 571 { 572 return getLocalFilters(); 573 } 574 } 575 576 //---------------------------------------------------------------------------- 577 // Resolver 578 //---------------------------------------------------------------------------- 579 580 /** 581 * Utility function supporting resolution of uris containing 'resource' or 582 * 'alias' schemes. If the supplied uri schem is 'resource' or 'alias' the 583 * reference is resolved to a artifact type, group and name from which a 584 * resource is resolved and the uri returned. If the scheme is resource 585 * the usri of the resource is returned. If the scheme is 'alias' a 586 * linkn alias is returned. If the scheme is not 'resource' or 'alias' 587 * the argument will be evaluated as a normal transit artifact uri 588 * specification. 589 * 590 * @param ref the uri argument 591 * @return the uri value 592 * @exception URISyntaxException if an error occurs during uri creation 593 */ 594 public URI toURI( final String ref ) throws URISyntaxException 595 { 596 Artifact spec = Artifact.createArtifact( ref ); 597 if( spec.isRecognized() ) 598 { 599 return spec.toURI(); 600 } 601 else if( ref.startsWith( "resource:" ) || ref.startsWith( "alias:" ) ) 602 { 603 String type = spec.getType(); 604 String group = spec.getGroup(); 605 String name = spec.getName(); 606 String path = group + "/" + name; 607 Library library = getLibrary(); 608 try 609 { 610 Resource resource = library.getResource( path ); 611 if( ref.startsWith( "resource:" ) ) 612 { 613 Artifact artifact = resource.getArtifact( type ); 614 return artifact.toURI(); 615 } 616 else 617 { 618 Artifact artifact = resource.getLinkArtifact( type ); 619 return artifact.toURI(); 620 } 621 } 622 catch( ResourceNotFoundException e ) 623 { 624 final String error = 625 "Unresolvable resource reference: " + path; 626 IllegalArgumentException iae = new IllegalArgumentException( error ); 627 iae.initCause( e ); 628 throw iae; 629 } 630 } 631 else 632 { 633 return spec.toURI(); 634 } 635 } 636 637 //---------------------------------------------------------------------------- 638 // implementation 639 //---------------------------------------------------------------------------- 640 641 Map getFilterMap() 642 { 643 return m_filters; 644 } 645 646 Filter[] getLocalFilters() 647 { 648 return (Filter[]) getFilterMap().values().toArray( new Filter[0] ); 649 } 650 651 /** 652 * Return an array of resource that are providers to this resource. 653 * @param scope the operational scope 654 * @param expand if true include transitive dependencies 655 * @param sort if true the array will sorted relative to dependencies 656 * @return the resource providers 657 */ 658 public Resource[] getProviders( final Scope scope, final boolean expand, final boolean sort ) 659 { 660 return getDefaultProviders( scope, expand, sort ); 661 } 662 663 /** 664 * Return an array of resource that are providers to this resource. If 665 * the supplied scope is BUILD the returned resource array is equivalent 666 * <src>getProviders( Scope.BUILD, .. )</src>. If the scope is RUNTIME 667 * the returned resource array includes BUILD and RUNTIME resources. If 668 * the scope is TEST the returned array includes BUILD, RUNTIME and TEST 669 * resources. 670 * @param scope the scope of aggregation to be applied to the selection 671 * @param expand if TRUE include transitive dependencies 672 * @param sort if true the array will sorted relative to dependencies 673 * @return the resource providers 674 */ 675 public Resource[] getAggregatedProviders( final Scope scope, final boolean expand, final boolean sort ) 676 { 677 return getAggregatedDefaultProviders( scope, expand, sort, false ); 678 } 679 680 /** 681 * Return a sorted and filtered array of providers. Resources not declaring 682 * the "jar" type as a produced type are excluded from selection. The 683 * resource array will include transitive dependencies. The method is 684 * suitable for the construction of build and test phase classloaders. 685 * 686 * @param scope the aggregation scope 687 * @return the scoped resource chain 688 */ 689 public Resource[] getClasspathProviders( final Scope scope ) 690 { 691 DefaultResource[] result = getAggregatedDefaultProviders( scope, true, true, true ); 692 List stack = new ArrayList(); 693 for( int i=0; i<result.length; i++ ) 694 { 695 DefaultResource resource = result[i]; 696 if( resource.isa( "jar" ) ) 697 { 698 stack.add( resource ); 699 } 700 } 701 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 702 } 703 704 /** 705 * Return an array of runtime providers filtered relative to a supplied 706 * classloading category. Resources not declaring the "jar" type as a 707 * produced type are excluded from selection. The resource array returned 708 * from this operation is a sorted transitive sequence excluding all 709 * resource references by any category higher than the supplied category. 710 * This method is typically used to construct information suitable for 711 * the gerneration of plugin metadata. 712 * 713 * @param category the classloader category 714 * @return the category scoped resource chain 715 */ 716 public Resource[] getClasspathProviders( final Category category ) 717 { 718 DefaultResource[] resources = getClasspathDefaultProviders( category ); 719 return sortDefaultResources( resources, Scope.RUNTIME ); 720 } 721 722 /** 723 * Return an array of resources that are consumers of this resource. 724 * @param expand if true the returned array includes consumers associated 725 * through transitive dependency relationships, otherwise the array is 726 * limited to direct consumers 727 * @param sort if true the array is sorted relative to depenency relationships 728 * @return the array of consumer projects 729 */ 730 public Resource[] getConsumers( final boolean expand, final boolean sort ) 731 { 732 return getDefaultConsumers( expand, sort ); 733 } 734 735 /** 736 * Return the underlying resource defintion. 737 * @return the resource directive 738 */ 739 public ResourceDirective getResourceDirective() 740 { 741 return m_directive; 742 } 743 744 /** 745 * Return a filename using the layout strategy employed by the cache. 746 * @param id the artifact type 747 * @return the filename 748 */ 749 public String getLayoutPath( final String id ) 750 { 751 Artifact artifact = getArtifact( id ); 752 return Transit.getInstance().getCacheLayout().resolveFilename( artifact ); 753 } 754 755 /** 756 * Return a directive suitable for publication as an external description. 757 * @param module the enclosing module 758 * @return the resource directive 759 */ 760 ResourceDirective exportResource( final DefaultModule module ) 761 { 762 if( null == m_directive ) 763 { 764 final String error = 765 "Cannot export from the root module."; 766 throw new UnsupportedOperationException( error ); 767 } 768 String name = getName(); 769 String version = getVersion(); 770 String basedir = null; 771 InfoDirective info = m_directive.getInfoDirective(); 772 TypeDirective[] types = m_directive.getTypeDirectives(); 773 TypeDirective[] exportedTypes = createExportedTypes( types ); 774 DependencyDirective[] dependencies = createDeps( module ); 775 Properties properties = getExportProperties(); 776 return ResourceDirective.createResourceDirective( 777 name, version, Classifier.EXTERNAL, basedir, 778 info, exportedTypes, dependencies, properties, null ); 779 } 780 781 TypeDirective[] createExportedTypes( final TypeDirective[] types ) 782 { 783 TypeDirective[] export = new TypeDirective[ types.length ]; 784 for( int i=0; i<export.length; i++ ) 785 { 786 TypeDirective type = types[i]; 787 String id = type.getID(); 788 Version version = type.getVersion(); 789 export[i] = new TypeDirective( id, version ); 790 } 791 return export; 792 } 793 794 private DependencyDirective[] createDeps( final DefaultModule module ) 795 { 796 ArrayList list = new ArrayList(); 797 createIncludeDirectives( module, list, Category.SYSTEM ); 798 createIncludeDirectives( module, list, Category.PUBLIC ); 799 createIncludeDirectives( module, list, Category.PROTECTED ); 800 createIncludeDirectives( module, list, Category.PRIVATE ); 801 if( list.size() == 0 ) 802 { 803 return new DependencyDirective[0]; 804 } 805 else 806 { 807 IncludeDirective[] includes = 808 (IncludeDirective[]) list.toArray( new IncludeDirective[0] ); 809 DependencyDirective runtime = 810 new DependencyDirective( Scope.RUNTIME, includes ); 811 return new DependencyDirective[]{runtime}; 812 } 813 } 814 815 boolean isaDescendant( final DefaultModule module ) 816 { 817 if( module == this ) 818 { 819 return true; 820 } 821 if( m_parent == null ) 822 { 823 return false; 824 } 825 else 826 { 827 if( m_parent == module ) 828 { 829 return true; 830 } 831 else 832 { 833 return m_parent.isaDescendant( module ); 834 } 835 } 836 } 837 838 private void createIncludeDirectives( 839 final DefaultModule module, final List list, final Category category ) 840 { 841 DefaultResource[] providers = 842 getDefaultProviders( Scope.RUNTIME, true, category ); 843 for( int i=0; i<providers.length; i++ ) 844 { 845 DefaultResource provider = providers[i]; 846 if( provider.isaDescendant( module ) ) 847 { 848 // create a ref 849 String path = provider.getResourcePath(); 850 IncludeDirective include = 851 new IncludeDirective( 852 IncludeDirective.REF, 853 category, 854 path, 855 null ); 856 list.add( include ); 857 } 858 else 859 { 860 // create a urn 861 862 Type[] types = provider.getTypes(); 863 for( int j=0; j<types.length; j++ ) 864 { 865 Type type = types[j]; 866 String label = type.getID(); 867 Artifact artifact = provider.getArtifact( label ); 868 String urn = artifact.toString(); 869 IncludeDirective include = 870 new IncludeDirective( 871 IncludeDirective.URI, 872 category, 873 urn, 874 null ); 875 list.add( include ); 876 } 877 } 878 } 879 } 880 881 //---------------------------------------------------------------------------- 882 // Object 883 //---------------------------------------------------------------------------- 884 885 /** 886 * Return a string representation of the resource in the form 'resource:[path]'. 887 * @return the string value 888 */ 889 public String toString() 890 { 891 if( null != m_directive ) 892 { 893 if( m_directive.isLocal() ) 894 { 895 return toString( "project" ); 896 } 897 } 898 return toString( "resource" ); 899 } 900 901 String toString( final String type ) 902 { 903 return type + ":" + getResourcePath() + "#" + getVersion(); 904 } 905 906 /** 907 * Compare this object with another. 908 * @param other the other object 909 * @return the comparitive index 910 */ 911 public int compareTo( final Object other ) 912 { 913 if( other instanceof DefaultResource ) 914 { 915 DefaultResource resource = (DefaultResource) other; 916 return getResourcePath().compareTo( resource.m_path ); 917 } 918 else 919 { 920 return -1; 921 } 922 } 923 924 //---------------------------------------------------------------------------- 925 // internals 926 //---------------------------------------------------------------------------- 927 928 /** 929 * Return the singlton library. 930 * @return the library 931 */ 932 DefaultLibrary getDefaultLibrary() 933 { 934 return m_library; 935 } 936 937 boolean isAnonymous() 938 { 939 if( null != m_directive ) 940 { 941 return m_directive.isAnonymous(); 942 } 943 return false; 944 } 945 946 boolean isLocal() 947 { 948 if( null != m_directive ) 949 { 950 return m_directive.isLocal(); 951 } 952 return false; 953 } 954 955 DefaultModule getDefaultParent() 956 { 957 if( null != m_parent ) 958 { 959 if( m_parent.isRoot() ) 960 { 961 return null; 962 } 963 } 964 return m_parent; 965 } 966 967 DefaultResource[] getAggregatedDefaultProviders( 968 final Scope scope, final boolean expanded, final boolean sort, final boolean flag ) 969 { 970 DefaultResource[] resources = 971 getAggregatedDefaultProviders( scope, expanded, flag ); 972 if( sort ) 973 { 974 return sortDefaultResources( resources, scope ); 975 } 976 else 977 { 978 Arrays.sort( resources ); 979 return resources; 980 } 981 } 982 983 DefaultResource[] getAggregatedDefaultProviders( 984 final Scope scope, final boolean expanded, final boolean flag ) 985 { 986 ArrayList list = new ArrayList(); 987 if( !flag ) 988 { 989 aggregateProviders( list, Scope.BUILD ); 990 } 991 if( scope.isGreaterThan( Scope.BUILD ) ) 992 { 993 aggregateProviders( list, Scope.RUNTIME ); 994 } 995 if( scope.isGreaterThan( Scope.RUNTIME ) ) 996 { 997 aggregateProviders( list, Scope.TEST ); 998 } 999 DefaultResource[] result = (DefaultResource[]) list.toArray( new DefaultResource[0] ); 1000 if( expanded ) 1001 { 1002 List visited = new ArrayList(); 1003 List stack = new ArrayList(); 1004 for( int i=0; i<result.length; i++ ) 1005 { 1006 DefaultResource resource = result[i]; 1007 expandDefaultResource( visited, stack, scope, resource ); 1008 } 1009 result = (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1010 } 1011 return result; 1012 } 1013 1014 private void aggregateProviders( final List list, final Scope scope ) 1015 { 1016 DefaultResource[] resources = getDefaultProviders( scope, false, null ); 1017 for( int i=0; i<resources.length; i++ ) 1018 { 1019 DefaultResource resource = resources[i]; 1020 if( !list.contains( resource ) ) 1021 { 1022 list.add( resource ); 1023 } 1024 } 1025 } 1026 1027 DefaultResource[] getDefaultProviders( 1028 final Scope scope, final boolean expanded, final boolean sort ) 1029 { 1030 DefaultResource[] resources = getDefaultProviders( scope, expanded, null ); 1031 if( sort ) 1032 { 1033 return sortDefaultResources( resources, scope ); 1034 } 1035 else 1036 { 1037 Arrays.sort( resources ); 1038 return resources; 1039 } 1040 } 1041 1042 DefaultResource[] getDefaultProviders( 1043 final Scope scope, final boolean expand, final Category category ) 1044 { 1045 ArrayList visited = new ArrayList(); 1046 ArrayList stack = new ArrayList(); 1047 DefaultResource[] providers = getLocalDefaultProviders( scope, category ); 1048 for( int i=0; i<providers.length; i++ ) 1049 { 1050 DefaultResource provider = providers[i]; 1051 if( expand ) 1052 { 1053 expandDefaultResource( visited, stack, scope, provider ); 1054 } 1055 else if( !stack.contains( provider ) ) 1056 { 1057 stack.add( provider ); 1058 } 1059 } 1060 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1061 } 1062 1063 DefaultResource[] getLocalDefaultProviders( final Scope scope, final Category category ) 1064 { 1065 if( null == m_directive ) 1066 { 1067 return new DefaultResource[0]; 1068 } 1069 IncludeDirective[] includes = getLocalIncludes( scope, category ); 1070 DefaultResource[] resources = new DefaultResource[ includes.length ]; 1071 for( int i=0; i<includes.length; i++ ) 1072 { 1073 IncludeDirective include = includes[i]; 1074 if( include.getMode().equals( IncludeDirective.URI ) ) 1075 { 1076 try 1077 { 1078 String value = include.getValue(); 1079 String urn = resolve( value ); 1080 Properties properties = include.getProperties(); 1081 resources[i] = m_library.getAnonymousResource( urn, properties ); 1082 } 1083 catch( URISyntaxException e ) 1084 { 1085 final String error = 1086 "Invalid uri value: " + include.getValue(); 1087 throw new RuntimeException( error, e ); 1088 } 1089 catch( InvalidNameException e ) 1090 { 1091 final String error = 1092 "An anonomous dependency include reference to [" 1093 + include 1094 + "] within the resource [" 1095 + getResourcePath() 1096 + "] could not be resolved."; 1097 throw new InvalidNameException( error, e ); 1098 } 1099 catch( Exception e ) 1100 { 1101 final String error = 1102 "Unexpected error during dynamic resource creation."; 1103 throw new RuntimeException( error, e ); 1104 } 1105 } 1106 else 1107 { 1108 String ref = getIncludeReference( include ); 1109 try 1110 { 1111 DefaultResource resource = m_library.getDefaultResource( ref ); 1112 resources[i] = resource; 1113 } 1114 catch( InvalidNameException e ) 1115 { 1116 if( null == category ) 1117 { 1118 final String error = 1119 "A dependency include [" 1120 + ref 1121 + "] within [" 1122 + this 1123 + "] referencing [" 1124 + ref 1125 + "] under the scope [" 1126 + scope 1127 + "] is unknown."; 1128 throw new InvalidNameException( error ); 1129 } 1130 else 1131 { 1132 final String error = 1133 "A dependency include within [" 1134 + this 1135 + "] referencing [" 1136 + ref 1137 + "] under the scope [" 1138 + scope 1139 + "] and category [" 1140 + category 1141 + "] is unknown."; 1142 throw new InvalidNameException( error ); 1143 } 1144 } 1145 } 1146 } 1147 return resources; 1148 } 1149 1150 private IncludeDirective[] getLocalIncludes( final Scope scope, final Category category ) 1151 { 1152 DependencyDirective dependency = m_directive.getDependencyDirective( scope ); 1153 if( null == category ) 1154 { 1155 return dependency.getIncludeDirectives(); 1156 } 1157 else 1158 { 1159 return dependency.getIncludeDirectives( category ); 1160 } 1161 } 1162 1163 private void expandDefaultResource( 1164 final List visited, final List stack, final Scope scope, final DefaultResource resource ) 1165 { 1166 if( visited.contains( resource ) ) 1167 { 1168 return; 1169 } 1170 else 1171 { 1172 visited.add( resource ); 1173 boolean flag = !scope.equals( Scope.BUILD ); 1174 DefaultResource[] providers = resource.getAggregatedDefaultProviders( scope, false, flag ); 1175 for( int i=0; i<providers.length; i++ ) 1176 { 1177 DefaultResource provider = providers[i]; 1178 expandDefaultResource( visited, stack, scope, provider ); 1179 } 1180 stack.add( resource ); 1181 } 1182 } 1183 1184 private String getIncludeReference( final IncludeDirective directive ) 1185 { 1186 if( null == m_parent ) 1187 { 1188 return directive.getValue(); 1189 } 1190 else 1191 { 1192 if( IncludeDirective.REF.equals( directive.getMode() ) ) 1193 { 1194 return directive.getValue(); 1195 } 1196 else 1197 { 1198 String path = m_parent.getResourcePath(); 1199 if( "".equals( path ) ) 1200 { 1201 return directive.getValue(); 1202 } 1203 else 1204 { 1205 String key = directive.getValue(); 1206 return path + "/" + key; 1207 } 1208 } 1209 } 1210 } 1211 1212 //---------------------------------------------------------------------------- 1213 // consumer concerns 1214 //---------------------------------------------------------------------------- 1215 1216 boolean isaConsumer( final DefaultResource resource ) 1217 { 1218 DefaultResource[] resources = getAggregatedDefaultProviders( Scope.TEST, false, false ); 1219 for( int i=0; i<resources.length; i++ ) 1220 { 1221 DefaultResource provider = resources[i]; 1222 if( resource.equals( provider ) ) 1223 { 1224 return true; 1225 } 1226 } 1227 return false; 1228 } 1229 1230 DefaultResource[] getDefaultConsumers( final boolean expand, final boolean sort ) 1231 { 1232 DefaultResource[] consumers = getDefaultConsumers( expand ); 1233 if( sort ) 1234 { 1235 return sortDefaultResources( consumers, Scope.TEST ); 1236 } 1237 else 1238 { 1239 return consumers; 1240 } 1241 } 1242 1243 DefaultResource[] getDefaultConsumers( final boolean expand ) 1244 { 1245 if( !expand ) 1246 { 1247 ArrayList list = new ArrayList(); 1248 DefaultResource[] resources = m_library.selectDefaultResources( "**/*" ); 1249 for( int i=0; i<resources.length; i++ ) 1250 { 1251 DefaultResource resource = resources[i]; 1252 if( !list.contains( resource ) && resource.isaConsumer( this ) ) 1253 { 1254 list.add( resource ); 1255 } 1256 } 1257 return (DefaultResource[]) list.toArray( new DefaultResource[0] ); 1258 } 1259 else 1260 { 1261 ArrayList visited = new ArrayList(); 1262 ArrayList stack = new ArrayList(); 1263 DefaultResource[] consumers = getDefaultConsumers( false ); 1264 for( int i=0; i<consumers.length; i++ ) 1265 { 1266 DefaultResource consumer = consumers[i]; 1267 processConsumer( visited, stack, consumer ); 1268 } 1269 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1270 } 1271 } 1272 1273 void processConsumer( final List visited, final List stack, final DefaultResource consumer ) 1274 { 1275 if( visited.contains( consumer ) ) 1276 { 1277 return; 1278 } 1279 visited.add( consumer ); 1280 stack.add( consumer ); 1281 DefaultResource[] resources = consumer.getDefaultConsumers( false, false ); 1282 for( int i=0; i<resources.length; i++ ) 1283 { 1284 DefaultResource resource = resources[i]; 1285 processConsumer( visited, stack, resource ); 1286 } 1287 } 1288 1289 //---------------------------------------------------------------------------- 1290 // classpath stuff 1291 //---------------------------------------------------------------------------- 1292 1293 /** 1294 * Construct an array of resources based on the RUNTIME scoped dependencies 1295 * associated with the supplied category. The implementation builds a list 1296 * of all preceeding categories as a basis for filtering the returned list ensuring 1297 * no duplicate references are returned. 1298 * @param category the runtime classloader category 1299 * @return the array of resources the define a classloader for the category 1300 */ 1301 private DefaultResource[] getClasspathDefaultProviders( final Category category ) 1302 { 1303 ArrayList list = new ArrayList(); 1304 for( int i=0; i<category.getValue(); i++ ) 1305 { 1306 Category c = Category.parse( i ); 1307 DefaultResource[] collection = 1308 getDefaultProviders( Scope.RUNTIME, true, c ); 1309 for( int j=0; j<collection.length; j++ ) 1310 { 1311 list.add( collection[j] ); 1312 } 1313 } 1314 DefaultResource[] resources = 1315 getDefaultProviders( Scope.RUNTIME, true, category ); 1316 ArrayList stack = new ArrayList(); 1317 for( int i=0; i<resources.length; i++ ) 1318 { 1319 DefaultResource resource = resources[i]; 1320 if( resource.isa( "jar" ) && !list.contains( resource ) ) 1321 { 1322 stack.add( resource ); 1323 } 1324 } 1325 1326 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1327 } 1328 1329 //---------------------------------------------------------------------------- 1330 // sorting relative to dependencies 1331 //---------------------------------------------------------------------------- 1332 1333 DefaultResource[] sortDefaultResources( final DefaultResource[] resources ) 1334 { 1335 return sortDefaultResources( resources, Scope.TEST ); 1336 } 1337 1338 DefaultResource[] sortDefaultResources( final DefaultResource[] resources, final Scope scope ) 1339 { 1340 ArrayList visited = new ArrayList(); 1341 ArrayList stack = new ArrayList(); 1342 for( int i=0; i<resources.length; i++ ) 1343 { 1344 DefaultResource resource = resources[i]; 1345 resource.sortDefaultResource( visited, stack, scope, resources ); 1346 } 1347 return (DefaultResource[]) stack.toArray( new DefaultResource[0] ); 1348 } 1349 1350 void sortDefaultResource( 1351 final List visited, final List stack, final Scope scope, final DefaultResource[] resources ) 1352 { 1353 if( visited.contains( this ) ) 1354 { 1355 return; 1356 } 1357 else 1358 { 1359 visited.add( this ); 1360 DefaultResource[] providers = 1361 getAggregatedDefaultProviders( scope, false, false ); 1362 for( int i=0; i<providers.length; i++ ) 1363 { 1364 DefaultResource provider = providers[i]; 1365 if( isaMember( resources, provider ) ) 1366 { 1367 provider.sortDefaultResource( visited, stack, scope, resources ); 1368 } 1369 } 1370 if( !stack.contains( this ) ) 1371 { 1372 stack.add( this ); 1373 } 1374 } 1375 } 1376 1377 boolean isaMember( final DefaultResource[] resources, final DefaultResource resource ) 1378 { 1379 for( int i=0; i<resources.length; i++ ) 1380 { 1381 DefaultResource r = resources[i]; 1382 if( resource == r ) 1383 { 1384 return true; 1385 } 1386 } 1387 return false; 1388 } 1389 1390 //---------------------------------------------------------------------------- 1391 // version utilities 1392 //---------------------------------------------------------------------------- 1393 1394 private String getStandardVersion() 1395 { 1396 String signature = getBuildSignature(); 1397 if( BOOTSTRAP.equals( signature ) ) 1398 { 1399 return BOOTSTRAP; 1400 } 1401 1402 if( isDecimal() ) 1403 { 1404 Version decimal = getDecimalVersion(); 1405 String spec = decimal.toString(); 1406 if( null == signature ) 1407 { 1408 return spec; 1409 } 1410 else 1411 { 1412 return spec + "-" + signature; 1413 } 1414 } 1415 else 1416 { 1417 if( null == signature ) 1418 { 1419 return SNAPSHOT; 1420 } 1421 else 1422 { 1423 return signature; 1424 } 1425 } 1426 } 1427 1428 private boolean isDecimal() 1429 { 1430 boolean isDecimal = Boolean.getBoolean( DECIMAL_VERSIONING_KEY ); 1431 isDecimal = getBooleanProperty( DECIMAL_VERSIONING_KEY, isDecimal ); 1432 return getBooleanProperty( LEGACY_DECIMAL_PREFIX_KEY, isDecimal ); 1433 } 1434 1435 private String getBuildSignature() 1436 { 1437 String system = System.getProperty( "build.signature", null ); 1438 return getProperty( "build.signature", system ); 1439 } 1440 1441 /* 1442 private String getBuildSignature() 1443 { 1444 String system = System.getProperty( "build.signature", null ); 1445 String value = getProperty( "build.signature", system ); 1446 if( null == value ) 1447 { 1448 return SNAPSHOT; 1449 } 1450 else if( value.equals( "project.timestamp" ) ) 1451 { 1452 return TIMESTAMP; 1453 } 1454 else 1455 { 1456 return value; 1457 } 1458 } 1459 */ 1460 1461 private int getMajorVersion() 1462 { 1463 return getIntegerProperty( "project.major.version", 0 ); 1464 } 1465 1466 private int getMinorVersion() 1467 { 1468 return getIntegerProperty( "project.minor.version", 0 ); 1469 } 1470 1471 private int getMicroVersion() 1472 { 1473 return getIntegerProperty( "project.micro.version", 0 ); 1474 } 1475 1476 /** 1477 * Return the UTC YYMMDD.HHMMSSS signature of a date. 1478 * @return the UTC date-stamp signature 1479 */ 1480 public static String getTimestamp() 1481 { 1482 return getTimestamp( new Date() ); 1483 } 1484 1485 /** 1486 * Return the UTC YYMMDD.HHMMSSS signature of a date. 1487 * @param date the date 1488 * @return the UTC date-stamp signature 1489 */ 1490 public static String getTimestamp( final Date date ) 1491 { 1492 final SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); 1493 sdf.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); 1494 return sdf.format( date ); 1495 } 1496 1497 //---------------------------------------------------------------------------- 1498 // other utilities 1499 //---------------------------------------------------------------------------- 1500 1501 private File getAnchor() 1502 { 1503 if( null != m_parent ) 1504 { 1505 File anchor = m_parent.getBaseDir(); 1506 if( null != anchor ) 1507 { 1508 return anchor; 1509 } 1510 } 1511 return m_library.getRootDirectory(); 1512 } 1513 1514 File getCanonicalFile( final File file ) 1515 { 1516 try 1517 { 1518 return file.getCanonicalFile(); 1519 } 1520 catch( IOException e ) 1521 { 1522 final String error = 1523 "internal error while attempting to convert the file [" 1524 + file 1525 + "] to its canonical representation."; 1526 throw new RuntimeException( error, e ); 1527 } 1528 } 1529 1530 private String getGroupName() 1531 { 1532 if( m_parent.isRoot() ) 1533 { 1534 return null; 1535 } 1536 else 1537 { 1538 return m_parent.getResourcePath(); 1539 } 1540 } 1541 }